Merge "Exclude version blocks by patterns" into main am: 2f5228f1dc
Original change: https://android-review.googlesource.com/c/platform/development/+/2745605
Change-Id: Ie744d567fdc221c83909d14bf19cc69e98f6b6eb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..60ec99d
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,8 @@
+# Per-project `repo upload` hook settings.
+# https://android.googlesource.com/platform/tools/repohooks
+
+[Options]
+ignore_merged_commits = true
+
+[Hook Scripts]
+winscope = ./tools/winscope/hooks/pre-upload ${PREUPLOAD_FILES}
diff --git a/build/Android.bp b/build/Android.bp
index 07d78e6..9c3d0e8 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -93,7 +93,6 @@
lib32: {
deps: [
"aapt",
- "aapt2",
"aidl",
"apksigner",
"bcc_compat",
@@ -108,6 +107,7 @@
},
lib64: {
deps: [
+ "aapt2",
"libwinpthread-1",
],
},
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java
index 0bb2095..20fb98e 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java
@@ -18,6 +18,7 @@
import android.app.IActivityManager;
import android.hardware.input.InputManager;
+import android.hardware.input.InputManagerGlobal;
import android.os.SystemClock;
import android.view.IWindowManager;
import android.view.InputDevice;
@@ -130,7 +131,7 @@
mRepeatCount, mMetaState, mDeviceId, mScanCode,
KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD);
}
- if (!InputManager.getInstance().injectInputEvent(keyEvent,
+ if (!InputManagerGlobal.getInstance().injectInputEvent(keyEvent,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT)) {
return MonkeyEvent.INJECT_FAIL;
}
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyMotionEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyMotionEvent.java
index f754a55..3eabc42 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeyMotionEvent.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyMotionEvent.java
@@ -18,6 +18,7 @@
import android.app.IActivityManager;
import android.hardware.input.InputManager;
+import android.hardware.input.InputManagerGlobal;
import android.os.SystemClock;
import android.util.SparseArray;
import android.view.IWindowManager;
@@ -185,7 +186,7 @@
Logger.out.println(msg.toString());
}
try {
- if (!InputManager.getInstance().injectInputEvent(me,
+ if (!InputManagerGlobal.getInstance().injectInputEvent(me,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT)) {
return MonkeyEvent.INJECT_FAIL;
}
diff --git a/ide/clion/CMakeLists.txt b/ide/clion/CMakeLists.txt
new file mode 100644
index 0000000..122939a
--- /dev/null
+++ b/ide/clion/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Will add_directory the given path only if the directory exists. If the directory doesn't exist,
+# it will look for either arm64-android or x86_64-android variants of the path. If it finds either, or both,
+# then it will add the newer of the two.
+function(try_add_subdir path)
+ if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${path}")
+ add_subdirectory(${path})
+ endif()
+
+ set(path_arm64 ${path}-arm64-android)
+ if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${path_arm64}")
+ set(has_arm64 TRUE)
+ else()
+ set(has_arm64 FALSE)
+ endif()
+
+ set(path_x64 ${path}-x86_64-android)
+ if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${path_x64}")
+ set(has_x64 TRUE)
+ else()
+ set(has_x64 FALSE)
+ endif()
+
+ if (${has_arm64} AND ${has_x64})
+ if ("${CMAKE_CURRENT_SOURCE_DIR}/${path_arm64}/CMakeLists.txt" IS_NEWER_THAN
+ "${CMAKE_CURRENT_SOURCE_DIR}/${path_x64}/CMakeLists.txt")
+ add_subdirectory(${path_arm64})
+ else()
+ add_subdirectory(${path_x64})
+ endif()
+ elseif(${has_arm64})
+ add_subdirectory(${path_arm64})
+ elseif(${has_x64})
+ add_subdirectory(${path_x64})
+ endif()
+endfunction()
\ No newline at end of file
diff --git a/ide/clion/external/minigbm/CMakeLists.txt b/ide/clion/external/minigbm/CMakeLists.txt
new file mode 100644
index 0000000..43f8a60
--- /dev/null
+++ b/ide/clion/external/minigbm/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.6)
+project(external_minigbm)
+add_subdirectory(cros_gralloc/aidl/android.hardware.graphics.allocator-service.minigbm-x86_64-android)
+add_subdirectory(cros_gralloc/mapper_stablec/mapper.minigbm-x86_64-android)
+add_subdirectory(cros_gralloc/gralloc4/libminigbm_gralloc4_utils-x86_64-android)
+add_subdirectory(libminigbm_gralloc-x86_64-android)
\ No newline at end of file
diff --git a/ide/clion/frameworks/base/CMakeLists.txt b/ide/clion/frameworks/base/CMakeLists.txt
index 14b8fb3..adc89c1 100644
--- a/ide/clion/frameworks/base/CMakeLists.txt
+++ b/ide/clion/frameworks/base/CMakeLists.txt
@@ -1,11 +1,13 @@
cmake_minimum_required(VERSION 3.6)
project(frameworks_base)
+include("${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt")
+
# HWUI
-add_subdirectory(libs/hwui/libhwui-arm64-android)
-add_subdirectory(libs/hwui/hwuimacro-arm64-android)
-add_subdirectory(libs/hwui/hwuimicro-arm64-android)
-add_subdirectory(libs/hwui/hwui_unit_tests-arm64-android)
+try_add_subdir(libs/hwui/libhwui)
+try_add_subdir(libs/hwui/hwuimacro)
+try_add_subdir(libs/hwui/hwuimicro)
+try_add_subdir(libs/hwui/hwui_unit_tests)
# JNI
-add_subdirectory(core/jni/libandroid_runtime-arm64-android)
+try_add_subdir(core/jni/libandroid_runtime)
diff --git a/ide/clion/frameworks/native/CMakeLists.txt b/ide/clion/frameworks/native/CMakeLists.txt
index ac0ed9f..8f98719 100644
--- a/ide/clion/frameworks/native/CMakeLists.txt
+++ b/ide/clion/frameworks/native/CMakeLists.txt
@@ -1,25 +1,28 @@
cmake_minimum_required(VERSION 3.6)
project(native)
-add_subdirectory(libs/gui/libgui-arm64-android)
-add_subdirectory(libs/gui/tests/libgui_test-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/layerproto/liblayers_proto-arm64-android)
-add_subdirectory(services/surfaceflinger/tests/IPC_test-arm64-android)
-add_subdirectory(services/surfaceflinger/tests/fakehwc/sffakehwc_test-arm64-android)
-add_subdirectory(services/surfaceflinger/tests/SurfaceFlinger_test-arm64-android)
-add_subdirectory(services/surfaceflinger/tests/vsync/test-vsync-events-arm64-android)
-add_subdirectory(services/surfaceflinger/tests/waitforvsync/test-waitforvsync-arm64-android)
-add_subdirectory(services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest-arm64-android)
-add_subdirectory(services/surfaceflinger/libSurfaceFlingerProp-arm64-android)
-add_subdirectory(services/surfaceflinger/sysprop/libSurfaceFlingerProperties-arm64-android)
-add_subdirectory(services/surfaceflinger/fuzzer/surfaceflinger_fuzzer-arm64-android)
-add_subdirectory(services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer-arm64-android)
-add_subdirectory(services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer-arm64-android)
+
+include("${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt")
+
+try_add_subdir(libs/gui/libgui)
+try_add_subdir(libs/gui/tests/libgui_test)
+try_add_subdir(libs/ui/libui)
+try_add_subdir(libs/renderengine/librenderengine)
+try_add_subdir(services/surfaceflinger/surfaceflinger)
+try_add_subdir(services/surfaceflinger/CompositionEngine/libcompositionengine_mocks)
+try_add_subdir(services/surfaceflinger/CompositionEngine/libcompositionengine_test)
+try_add_subdir(services/surfaceflinger/CompositionEngine/libcompositionengine)
+try_add_subdir(services/surfaceflinger/FrameTimeline/libframetimeline)
+try_add_subdir(services/surfaceflinger/TimeStats/timestatsproto/libtimestats_proto)
+try_add_subdir(services/surfaceflinger/TimeStats/libtimestats)
+try_add_subdir(services/surfaceflinger/layerproto/liblayers_proto)
+try_add_subdir(services/surfaceflinger/tests/IPC_test)
+try_add_subdir(services/surfaceflinger/tests/fakehwc/sffakehwc_test)
+try_add_subdir(services/surfaceflinger/tests/SurfaceFlinger_test)
+try_add_subdir(services/surfaceflinger/tests/vsync/test-vsync-events)
+try_add_subdir(services/surfaceflinger/tests/waitforvsync/test-waitforvsync)
+try_add_subdir(services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest)
+try_add_subdir(services/surfaceflinger/libSurfaceFlingerProp)
+try_add_subdir(services/surfaceflinger/sysprop/libSurfaceFlingerProperties)
+try_add_subdir(services/surfaceflinger/fuzzer/surfaceflinger_fuzzer)
+try_add_subdir(services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer)
+try_add_subdir(services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer)
diff --git a/ide/clion/hardware/interfaces/graphics/CMakeLists.txt b/ide/clion/hardware/interfaces/graphics/CMakeLists.txt
new file mode 100644
index 0000000..fd57659
--- /dev/null
+++ b/ide/clion/hardware/interfaces/graphics/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.6)
+project(hardware_graphics)
+
+include("${CMAKE_CURRENT_SOURCE_DIR}/../../../CMakeLists.txt")
+
+try_add_subdir(allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest)
+try_add_subdir(allocator/aidl/android.hardware.graphics.allocator-V2-ndk)
+try_add_subdir(composer/aidl/android.hardware.graphics.composer3-V2-ndk)
+try_add_subdir(composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest)
+try_add_subdir(common/aidl/android.hardware.graphics.common-V4-ndk)
+try_add_subdir(mapper/stable-c/libimapper_providerutils_tests)
+try_add_subdir(mapper/stable-c/VtsHalGraphicsMapperStableC_TargetTest)
+try_add_subdir(mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest)
\ No newline at end of file
diff --git a/samples/ApiDemos/res/drawable/ic_eject.xml b/samples/ApiDemos/res/drawable/ic_eject.xml
new file mode 100644
index 0000000..37648b3
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/ic_eject.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M200,760L200,680L760,680L760,760L200,760ZM214,600L480,200L746,600L214,600ZM480,520L480,520L480,520L480,520ZM362,520L598,520L480,344L362,520Z"/>
+</vector>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index 9c981ec..75254d4 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -70,6 +70,7 @@
<string name="enter_content_pip">Enter content PiP</string>
<string name="enter_picture_in_picture">Manually enter PiP</string>
<string name="action_custom_close">Close PiP</string>
+ <string name="action_move_to_back">Background PiP</string>
<string name="label_current_position">Current position</string>
<string name="label_exit_position">Exit position</string>
<string name="label_position_start">Start</string>
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PictureInPicture.java b/samples/ApiDemos/src/com/example/android/apis/app/PictureInPicture.java
index da06cf7..4819d80 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/PictureInPicture.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PictureInPicture.java
@@ -42,7 +42,6 @@
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.Spinner;
@@ -66,6 +65,7 @@
private static final int TABLET_BREAK_POINT_DP = 700;
private static final String ACTION_CUSTOM_CLOSE = "demo.pip.custom_close";
+ private static final String ACTION_MOVE_TO_BACK = "demo.pip.move_to_back";
private final BroadcastReceiver mRemoteActionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -73,6 +73,9 @@
case ACTION_CUSTOM_CLOSE:
finish();
break;
+ case ACTION_MOVE_TO_BACK:
+ moveTaskToBack(false /* nonRoot */);
+ break;
}
}
};
@@ -110,6 +113,7 @@
private Spinner mAspectRatioSpinner;
private List<RemoteAction> mPipActions;
private RemoteAction mCloseAction;
+ private RemoteAction mMoveToBackAction;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -264,19 +268,29 @@
private void setupPipActions() {
final IntentFilter remoteActionFilter = new IntentFilter();
+ mPipActions = new ArrayList<>();
+
remoteActionFilter.addAction(ACTION_CUSTOM_CLOSE);
- registerReceiver(mRemoteActionReceiver, remoteActionFilter);
- final Intent intent = new Intent(ACTION_CUSTOM_CLOSE).setPackage(getPackageName());
+ final Intent closeIntent = new Intent(ACTION_CUSTOM_CLOSE).setPackage(getPackageName());
mCloseAction = new RemoteAction(
Icon.createWithResource(this, R.drawable.ic_call_end),
getString(R.string.action_custom_close),
getString(R.string.action_custom_close),
- PendingIntent.getBroadcast(this, 0 /* requestCode */, intent,
+ PendingIntent.getBroadcast(this, 0 /* requestCode */, closeIntent,
FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
-
- // Add close action as a regular PiP action
- mPipActions = new ArrayList<>(1);
mPipActions.add(mCloseAction);
+
+ remoteActionFilter.addAction(ACTION_MOVE_TO_BACK);
+ final Intent backIntent = new Intent(ACTION_MOVE_TO_BACK).setPackage(getPackageName());
+ mMoveToBackAction = new RemoteAction(
+ Icon.createWithResource(this, R.drawable.ic_eject),
+ getString(R.string.action_move_to_back),
+ getString(R.string.action_move_to_back),
+ PendingIntent.getBroadcast(this, 0 /* requestCode */, backIntent,
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
+ mPipActions.add(mMoveToBackAction);
+
+ registerReceiver(mRemoteActionReceiver, remoteActionFilter);
}
private void setupPictureInPictureLayout() {
diff --git a/samples/ToyVpn/AndroidManifest.xml b/samples/ToyVpn/AndroidManifest.xml
index 5f1cc2b..3d430ab 100644
--- a/samples/ToyVpn/AndroidManifest.xml
+++ b/samples/ToyVpn/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.example.android.toyvpn">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-sdk android:minSdkVersion="14"/>
@@ -33,7 +34,9 @@
</activity>
<service android:name=".ToyVpnService"
- android:permission="android.permission.BIND_VPN_SERVICE">
+ android:permission="android.permission.BIND_VPN_SERVICE"
+ android:foregroundServiceType="specialUse">
+ <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
diff --git a/samples/VoiceInteractionService/Android.bp b/samples/VoiceInteractionService/Android.bp
new file mode 100644
index 0000000..13974f2
--- /dev/null
+++ b/samples/VoiceInteractionService/Android.bp
@@ -0,0 +1,22 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+ name: "SampleVoiceInteractor",
+ srcs: ["**/*.java"],
+ min_sdk_version: "30",
+ target_sdk_version: "30",
+ sdk_version: "system_current",
+ privileged: true,
+ static_libs: [
+ "androidx.annotation_annotation",
+ ],
+}
+
+prebuilt_etc {
+ name: "com.example.android.voiceinteractor.xml",
+ src: "com.example.android.voiceinteractor.xml",
+ sub_dir: "permissions",
+ filename_from_src: true,
+}
diff --git a/samples/VoiceInteractionService/AndroidManifest.xml b/samples/VoiceInteractionService/AndroidManifest.xml
new file mode 100755
index 0000000..a8c49e1
--- /dev/null
+++ b/samples/VoiceInteractionService/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.voiceinteractor">
+
+ <application android:label="@string/app_name">
+ <profileable android:shell="true"/>
+ <activity android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <service
+ android:name=".SampleVoiceInteractionService"
+ android:label="@string/app_name"
+ android:permission="android.permission.BIND_VOICE_INTERACTION"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.voice.VoiceInteractionService" />
+ </intent-filter>
+ <meta-data
+ android:name="android.voice_interaction"
+ android:resource="@xml/voice_interaction" />
+ </service>
+ <service
+ android:name=".SampleHotwordDetectionService"
+ android:permission="android.permission.BIND_HOTWORD_DETECTION_SERVICE"
+ android:isolatedProcess="true"
+ android:allowSharedIsolatedProcess="true"
+ android:exported="true">
+ </service>
+ <service
+ android:name=".SampleVisualQueryDetectionService"
+ android:permission="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE"
+ android:isolatedProcess="true"
+ android:allowSharedIsolatedProcess="true"
+ android:exported="true">
+ </service>
+ </application>
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.RECORD_BACKGROUND_AUDIO" />
+ <uses-permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD" />
+ <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
+</manifest>
diff --git a/samples/VoiceInteractionService/README.md b/samples/VoiceInteractionService/README.md
new file mode 100644
index 0000000..92a12cf
--- /dev/null
+++ b/samples/VoiceInteractionService/README.md
@@ -0,0 +1,93 @@
+setup:
+
+(If a log error "VisualQueryDetector is only available if multiple detectors are allowed" , set target_sdk_version: "10000" in Android.bp for now.)
+1. Set the KEYPHRASE constant in SampleVoiceInteractionService.java to something the device's
+ default assistant supports.
+2. m -j SampleVoiceInteractor
+4. adb root; adb remount
+5. adb push development/samples/VoiceInteractionService/com.example.android.voiceinteractor.xml /system/etc/permissions/com.example.android.voiceinteractor.xml
+6. adb shell mkdir /system/priv-app/SampleVoiceInteractor
+7. adb push out/target/product/$TARGET_PRODUCT/system/priv-app/SampleVoiceInteractor/SampleVoiceInteractor.apk /system/priv-app/SampleVoiceInteractor/
+8. adb reboot
+9. Go to the sample app info/settings.
+10. Tap on Permissions and grant Mic access.
+11. Reboot.
+12. Set the "Digital assistant app" to "Sample Voice Interactor" in the Android settings
+13. Check for this in the logs to make sure it worked:
+ com.example.android.voiceinteractor I/VIS: onAvailabilityChanged: 2
+14. If it didn't, check if the pregrant worked:
+ adb shell dumpsys package com.example.android.voiceinteractor | grep CAPTURE_AUDIO_HOTWORD
+
+Iterating:
+* adb install like usual
+* If syncing changes to the system image, either first copy the permissions file into
+ out/target/product/system/etc/permissions/ or push it again after syncing. Sometimes you might
+ need to uninstall the app (go to the sample app info/settings -> 3 dots menu -> uninstall
+ updates).
+
+to test:
+1. Say "1,2,Ok Poodle,3,4.."
+2. Check the logs for the app and wait till it finishes recording.
+3. Either check the logs for the sampled bytes to match, e.g. "sample=[95, 2, 97, ...]" should
+ appear twice; or open the sample app activity and click the button to play back the recorded
+ audio.
+Tap directRecord to simulate the non-DSP case (must be done after a dsp trigger since it
+ reuses the previous data).
+
+Debugging:
+* Set DEBUG to true in AlwaysOnHotwordDetector
+* uncomment LOG_NDEBUG lines at the top in AudioFlinger.cpp, Threads.cpp, Tracks.cpp,
+ AudioPolicyInterfaceImpl.cpp, AudioPolicyService.cpp
+* Use this logcat filter:
+ com.example.android.voiceinteractor|AlwaysOnHotword|SoundTrigger|RecordingActivityMonitor|soundtrigger|AudioPolicyManager|AudioFlinger|AudioPolicyIntefaceImpl|AudioPolicyService|VIS|SHotwordDetectionSrvc|Hotword-AudioUtils
+
+Collecting trace events: \
+Trace events are used throughout the test app to measure the time it takes to read the AudioRecord
+data in both the VoiceInteractionService and the trusted HotwordDetectionService. This section can
+be used as a guide to collect and observe this trace data.
+
+* Trace events:
+ * 'VIS.onDetected' and 'HDS.onDetected'
+ * 'VIS.createAudioRecord' and 'HDS.createAudioRecord'
+ * 'VIS.startRecording' and 'HDS.startRecording'
+ * 'AudioUtils.read' and 'AudioRecord.read'
+ * 'AudioUtils.bytesRead'
+ * Counter trace value increasing as the AudioUtils.read call progresses. This value is reset after each new call.
+
+* How to capture a trace:
+ * Follow this guide or a similar one: https://developer.android.com/topic/performance/tracing/on-device
+ * Open https://perfetto.dev/#/running.md and upload a trace report
+ * Search for the events manually or run the below example SQL query to pull out the events.
+
+* Perfetto trace SQL query
+ * How to run a SQL query: https://perfetto.dev/docs/quickstart/trace-analysis
+ * Covers both command line and HTML implementations
+```
+WITH
+ audio_events AS (
+ SELECT
+ ts,
+ (dur / 1000000) as dur_ms,
+ name
+ FROM
+ slice
+ WHERE
+ (name LIKE "%AudioUtils.read%"
+ OR name LIKE "%AudioRecord.read%"
+ OR name LIKE "%onDetected%"
+ OR name LIKE "%startRecording%"
+ OR name LIKE "%createAudioRecord%")
+ ),
+ audio_counters AS (
+ SELECT ts, name, value
+ FROM counter
+ INNER JOIN track ON counter.track_id = track.id
+ WHERE name LIKE "%AudioUtils.bytesRead%"
+ )
+SELECT ts, 'event' as type, name, dur_ms as value
+FROM audio_events
+UNION ALL
+SELECT ts, 'counter' as type, name, value
+FROM audio_counters
+ORDER BY ts
+```
\ No newline at end of file
diff --git a/samples/VoiceInteractionService/com.example.android.voiceinteractor.xml b/samples/VoiceInteractionService/com.example.android.voiceinteractor.xml
new file mode 100644
index 0000000..ea3647c
--- /dev/null
+++ b/samples/VoiceInteractionService/com.example.android.voiceinteractor.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+
+<permissions>
+ <privapp-permissions package="com.example.android.voiceinteractor">
+ <permission name="android.permission.CAPTURE_AUDIO_HOTWORD"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_HOTWORD_DETECTION"/>
+ </privapp-permissions>
+</permissions>
diff --git a/samples/VoiceInteractionService/lint-baseline.xml b/samples/VoiceInteractionService/lint-baseline.xml
new file mode 100644
index 0000000..bb6e9a4
--- /dev/null
+++ b/samples/VoiceInteractionService/lint-baseline.xml
@@ -0,0 +1,466 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.media.AudioRecord#getMaxSharedAudioHistoryMillis`"
+ errorLine1=" .setMaxSharedAudioHistoryMillis(AudioRecord.getMaxSharedAudioHistoryMillis())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="65"
+ column="61"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.media.AudioRecord#shareAudioHistory`"
+ errorLine1=' record.shareAudioHistory("com.example.android.voiceinteractor", 0))'
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="128"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.media.AudioRecord.Builder#setMaxSharedAudioHistoryMillis`"
+ errorLine1=" .setMaxSharedAudioHistoryMillis(AudioRecord.getMaxSharedAudioHistoryMillis())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="65"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.media.AudioRecord.Builder#setSharedAudioEvent`"
+ errorLine1=" .setSharedAudioEvent(eventPayload.getHotwordDetectedResult().getMediaSyncEvent())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="83"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.AlwaysOnHotwordDetector.EventPayload#getHotwordDetectedResult`"
+ errorLine1=" .setSharedAudioEvent(eventPayload.getHotwordDetectedResult().getMediaSyncEvent())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="83"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetectedResult#getMediaSyncEvent`"
+ errorLine1=" .setSharedAudioEvent(eventPayload.getHotwordDetectedResult().getMediaSyncEvent())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="83"
+ column="78"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetectedResult.Builder#build`"
+ errorLine1=" .build());"
+ errorLine2=" ~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="130"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetectedResult.Builder#setHotwordPhraseId`"
+ errorLine1=" .setHotwordPhraseId(getKeyphraseId(eventPayload))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="129"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetectedResult.Builder#setMediaSyncEvent`"
+ errorLine1=" .setMediaSyncEvent("
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="127"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetectionService.Callback#onDetected`"
+ errorLine1=" callback.onDetected("
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="125"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetectionService.Callback#onRejected`"
+ errorLine1=" callback.onRejected(new HotwordRejectedResult.Builder().build());"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="111"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetector#startRecognition`"
+ errorLine1=" mHotwordDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="253"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetector#startRecognition`"
+ errorLine1=" mHotwordDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="318"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetector#startRecognition`"
+ errorLine1=" mService.mHotwordDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/MainActivity.java"
+ line="57"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetector#startRecognition`"
+ errorLine1=" mHotwordDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="207"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetector#startRecognition`"
+ errorLine1=" mHotwordDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="286"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordDetector#startRecognition`"
+ errorLine1=" mHotwordDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="296"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.HotwordRejectedResult.Builder#build`"
+ errorLine1=" callback.onRejected(new HotwordRejectedResult.Builder().build());"
+ errorLine2=" ~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="111"
+ column="69"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.service.voice.VoiceInteractionService#createAlwaysOnHotwordDetector`"
+ errorLine1=" mHotwordDetector = createAlwaysOnHotwordDetector(DSP_MODEL_KEYPHRASE,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="105"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.service.voice.HotwordDetectedResult.Builder`"
+ errorLine1=" new HotwordDetectedResult.Builder()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="126"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.service.voice.HotwordRejectedResult.Builder`"
+ errorLine1=" callback.onRejected(new HotwordRejectedResult.Builder().build());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="111"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetectionService#finishQuery`"
+ errorLine1=" finishQuery();"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java"
+ line="146"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetectionService#gainedAttention`"
+ errorLine1=" gainedAttention();"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java"
+ line="124"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetectionService#lostAttention`"
+ errorLine1=" lostAttention();"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java"
+ line="135"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetectionService#streamQuery`"
+ errorLine1=" streamQuery(FAKE_QUERY);"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java"
+ line="145"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetector#startRecognition`"
+ errorLine1=" mVisualQueryDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="144"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetector#startRecognition`"
+ errorLine1=" mVisualQueryDetector.startRecognition();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="155"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 30): `android.service.voice.VoiceInteractionService#createVisualQueryDetector`"
+ errorLine1=" mVisualQueryDetector = createVisualQueryDetector(null, null,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="324"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast from `AlwaysOnHotwordDetector` to `HotwordDetector` requires API level 31 (current min is 30)"
+ errorLine1=" mHotwordDetector = createAlwaysOnHotwordDetector(DSP_MODEL_KEYPHRASE,"
+ errorLine2=" ^">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="105"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast from `HotwordDetector` to `AlwaysOnHotwordDetector` requires API level 31 (current min is 30)"
+ errorLine1=" enrollIntent = ((AlwaysOnHotwordDetector) mHotwordDetector).createEnrollIntent();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="186"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.service.voice.HotwordDetectionService`"
+ errorLine1="public class SampleHotwordDetectionService extends HotwordDetectionService {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java"
+ line="41"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetectionService`"
+ errorLine1="public class SampleVisualQueryDetectionService extends VisualQueryDetectionService {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java"
+ line="55"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 34 (current min is 30): `android.service.voice.VisualQueryDetector.Callback`"
+ errorLine1=" class VisualQueryDetectorCallback implements VisualQueryDetector.Callback {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="123"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="145"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (HotwordDetector.IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/MainActivity.java"
+ line="58"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="156"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="187"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="254"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="319"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="208"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="287"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Exception requires API level 34 (current min is 30): `android.service.voice.HotwordDetector.IllegalDetectorStateException`"
+ errorLine1=" } catch (IllegalDetectorStateException e) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="development/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java"
+ line="297"
+ column="22"/>
+ </issue>
+
+</issues>
\ No newline at end of file
diff --git a/samples/VoiceInteractionService/res/layout/main_activity.xml b/samples/VoiceInteractionService/res/layout/main_activity.xml
new file mode 100644
index 0000000..e3f1ede
--- /dev/null
+++ b/samples/VoiceInteractionService/res/layout/main_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2021 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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/buffer1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="buffer1" />
+ <Button
+ android:id="@+id/buffer2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="buffer2" />
+ <Button
+ android:id="@+id/startReco"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="startRecognition" />
+ <Button
+ android:id="@+id/directRecord"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="direct record" />
+
+<!-- <ScrollView-->
+<!-- android:layout_width="match_parent"-->
+<!-- android:layout_height="match_parent">-->
+
+<!-- <TextView-->
+<!-- android:layout_width="match_parent"-->
+<!-- android:layout_height="wrap_content"-->
+<!-- android:text="test" />-->
+<!-- </ScrollView>-->
+</LinearLayout>
diff --git a/samples/VoiceInteractionService/res/values/strings.xml b/samples/VoiceInteractionService/res/values/strings.xml
new file mode 100644
index 0000000..1b7fbdd
--- /dev/null
+++ b/samples/VoiceInteractionService/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<resources>
+ <string name="app_name">Sample Voice Interactor</string>
+</resources>
diff --git a/samples/VoiceInteractionService/res/xml/voice_interaction.xml b/samples/VoiceInteractionService/res/xml/voice_interaction.xml
new file mode 100644
index 0000000..2c4a5d9
--- /dev/null
+++ b/samples/VoiceInteractionService/res/xml/voice_interaction.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:sessionService=""
+ android:hotwordDetectionService="com.example.android.voiceinteractor.SampleHotwordDetectionService"
+ android:visualQueryDetectionService="com.example.android.voiceinteractor.SampleVisualQueryDetectionService"
+ android:recognitionService=""
+ android:settingsActivity=""
+ android:supportsAssist="true"
+ android:supportsLocalInteraction="true" />
\ No newline at end of file
diff --git a/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/AudioUtils.java b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/AudioUtils.java
new file mode 100644
index 0000000..ad70b17
--- /dev/null
+++ b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/AudioUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.voiceinteractor;
+
+import android.media.AudioRecord;
+import android.os.Trace;
+import android.util.Log;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+public class AudioUtils {
+ private static final String TAG = "Hotword-AudioUtils";
+ private static final Duration AUDIO_RECORD_READ_DURATION = Duration.ofSeconds(1);
+
+ static int read(AudioRecord record, int bytesPerSecond, float secondsToRead, byte[] buffer) {
+ Log.i(TAG, "read(): bytesPerSecond=" + bytesPerSecond
+ + ", secondsToRead=" + secondsToRead + ", bufferSize=" + buffer.length);
+ int numBytes = 0;
+ int nextSecondToSample = 0;
+ while (true) {
+ Trace.beginAsyncSection("AudioRecord.read", 0);
+ int bytesRead = record.read(buffer, numBytes,
+ (int) (bytesPerSecond * AUDIO_RECORD_READ_DURATION.getSeconds()));
+ Trace.endAsyncSection("AudioRecord.read", 0);
+ Log.i(TAG, "AudioRecord.read offset=" + numBytes + ", size="
+ + (bytesPerSecond * AUDIO_RECORD_READ_DURATION.getSeconds()));
+ numBytes += bytesRead;
+
+ if (bytesRead <= 0) {
+ Log.i(TAG, "Finished reading, last read()=" + bytesRead);
+ break;
+ }
+ int curSecond = numBytes / bytesPerSecond;
+ if (curSecond == nextSecondToSample
+ && numBytes > (bytesPerSecond * curSecond) + 10) {
+ Log.i(TAG, "sample=" + Arrays.toString(
+ Arrays.copyOfRange(
+ buffer, bytesPerSecond * curSecond,
+ (bytesPerSecond * curSecond) + 10)));
+ nextSecondToSample++;
+ }
+ if ((numBytes * 1.0 / bytesPerSecond) >= secondsToRead) {
+ Log.i(TAG, "recorded enough. stopping. bytesRead=" + numBytes
+ + ", secondsRead=" + (numBytes * 1.0 / bytesPerSecond));
+ break;
+ }
+ }
+ return numBytes;
+ }
+}
diff --git a/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/MainActivity.java b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/MainActivity.java
new file mode 100644
index 0000000..483a36e
--- /dev/null
+++ b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/MainActivity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2021 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.voiceinteractor;
+
+import static android.media.AudioTrack.PLAYSTATE_PAUSED;
+import static android.media.AudioTrack.PLAYSTATE_STOPPED;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.service.voice.HotwordDetector;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+public class MainActivity extends Activity {
+ private static final String TAG = "VIS";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main_activity);
+
+ attachClickListener(R.id.buffer1, "1");
+ attachClickListener(R.id.buffer2, "2");
+
+ Button button = (Button) findViewById(R.id.startReco);
+ button.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mService == null) {
+ Log.e(TAG, "No service");
+ return;
+ }
+ mService.mHotwordDetector.startRecognition();
+ }
+ });
+ button = (Button) findViewById(R.id.directRecord);
+ button.setOnClickListener(v -> {
+ if (mService == null) {
+ Log.e(TAG, "No service");
+ return;
+ }
+ mService.mHotwordDetectorCallback.onDetected(mService.mLastPayload, true);
+ });
+ }
+
+ private void attachClickListener(int id, String key) {
+ Button button = (Button) findViewById(id);
+ button.setOnClickListener(v -> {
+ if (mService == null) {
+ Log.e(TAG, "No service");
+ return;
+ }
+ playAudio(mService.mData.getByteArray(key));
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ // Bind to LocalService
+ Intent intent = new Intent(this, SampleVoiceInteractionService.class).setAction("local");
+ bindService(intent, connection, Context.BIND_AUTO_CREATE);
+ }
+
+ SampleVoiceInteractionService mService;
+ boolean mBound = false;
+
+ private final ServiceConnection connection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName className,
+ IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ SampleVoiceInteractionService.LocalBinder binder = (SampleVoiceInteractionService.LocalBinder) service;
+ mService = binder.getService();
+ mBound = true;
+ Log.i(TAG, "Connected to local service");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ mService = null;
+ mBound = false;
+ }
+ };
+
+ private void playAudio(byte[] buffer) {
+ AudioTrack track = new AudioTrack(
+ new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+// .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD)
+ .build(),
+ new AudioFormat.Builder()
+ .setChannelMask(AudioFormat.CHANNEL_IN_DEFAULT)
+ .setSampleRate(mService.mAudioFormat.getSampleRate())
+ .setEncoding(mService.mAudioFormat.getEncoding())
+ .build(),
+// mService.mAudioFormat,
+ buffer.length,
+ AudioTrack.MODE_STATIC,
+ AudioManager.AUDIO_SESSION_ID_GENERATE
+ );
+ Log.i(TAG, "track state=" + track.getState());
+ if (track.getState() == AudioTrack.STATE_UNINITIALIZED) {
+ return;
+ }
+ track.write(buffer, 0, buffer.length);
+// track.setNotificationMarkerPosition(track.getP)
+ track.play();
+ // TODO: Doesn't work.. fix the releasing.
+ track.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() {
+
+ @Override
+ public void onMarkerReached(AudioTrack track) {
+
+ }
+
+ @Override
+ public void onPeriodicNotification(AudioTrack track) {
+ if (track.getPlayState() == PLAYSTATE_STOPPED
+ || track.getPlayState() == PLAYSTATE_PAUSED) {
+ Log.i(TAG, "Stopped/paused playback; releasing.");
+ track.release();
+ }
+ }
+ });
+// try {
+// Thread.sleep(4000);
+// } catch (InterruptedException e) {
+// Thread.interrupted();
+// throw new RuntimeException(e);
+// }
+// track.release();
+
+// MediaPlayer player = new MediaPlayer();
+// player.setAudioAttributes(
+// new AudioAttributes.Builder()
+// .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+// .setUsage(AudioAttributes.USAGE_MEDIA)
+// .set
+// .build());
+// player.setDataSource(
+// "data:audio/mp3;base64,"
+//// new ByteArrayMediaSource(buffer)
+// );
+// try {
+// player.prepare();
+// } catch (IOException e) {
+// Log.e(TAG, "Failed to play: " + e);
+// }
+// player.start();
+ }
+
+// private static class ByteArrayMediaSource extends MediaDataSource {
+// final byte[] mData;
+//
+// public ByteArrayMediaSource(byte[] data) {
+// mData = data;
+// }
+//
+// @Override
+// public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
+// if (position >= mData.length) {
+// return -1; // end of stream
+// }
+// if (position + size > mData.length) {
+// size = (int) (mData.length - position);
+// }
+//
+// System.arraycopy(mData, (int) position, buffer, offset, size);
+// return size;
+// }
+//
+// @Override
+// public long getSize() throws IOException {
+// return 0;
+// }
+//
+// @Override
+// public void close() throws IOException {
+//
+// }
+// }
+}
diff --git a/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java
new file mode 100644
index 0000000..80577a3
--- /dev/null
+++ b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleHotwordDetectionService.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 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.voiceinteractor;
+
+import android.media.AudioAttributes;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.os.SharedMemory;
+import android.os.Trace;
+import android.service.voice.AlwaysOnHotwordDetector;
+import android.service.voice.HotwordDetectedResult;
+import android.service.voice.HotwordDetectionService;
+import android.service.voice.HotwordRejectedResult;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.time.Duration;
+import java.util.function.IntConsumer;
+
+public class SampleHotwordDetectionService extends HotwordDetectionService {
+ static final String TAG = "SHotwordDetectionSrvc";
+
+ // AudioRecord config
+ private static final Duration AUDIO_RECORD_BUFFER_DURATION = Duration.ofSeconds(5);
+ private static final Duration DSP_AUDIO_READ_DURATION = Duration.ofSeconds(3);
+ private static final Duration AUDIO_RECORD_RELEASE_TIMEOUT = Duration.ofSeconds(10);
+
+ private static AudioRecord createAudioRecord(AlwaysOnHotwordDetector.EventPayload eventPayload,
+ int bytesPerSecond,
+ int sessionId) {
+ int audioRecordBufferSize = getBufferSizeInBytes(bytesPerSecond,
+ AUDIO_RECORD_BUFFER_DURATION.getSeconds());
+ Log.d(TAG, "creating AudioRecord: bytes=" + audioRecordBufferSize
+ + ", lengthSeconds=" + (audioRecordBufferSize / bytesPerSecond));
+ return new AudioRecord.Builder()
+ .setAudioAttributes(
+ new AudioAttributes.Builder()
+ .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD)
+ // TODO see what happens if this is too small
+ .build())
+ .setAudioFormat(eventPayload.getCaptureAudioFormat())
+ .setBufferSizeInBytes(audioRecordBufferSize)
+ .setSessionId(sessionId)
+ .setMaxSharedAudioHistoryMillis(AudioRecord.getMaxSharedAudioHistoryMillis())
+ .build();
+ }
+
+ private static int getBufferSizeInBytes(int bytesPerSecond, float bufferLengthSeconds) {
+ return (int) (bytesPerSecond * bufferLengthSeconds);
+ }
+
+ @Override
+ public void onUpdateState(@Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory, long callbackTimeoutMillis,
+ @Nullable IntConsumer statusCallback) {
+ Log.i(TAG, "onUpdateState");
+ if (statusCallback != null) {
+ statusCallback.accept(0);
+ }
+ }
+
+ @Override
+ public void onDetect(
+ @NonNull AlwaysOnHotwordDetector.EventPayload eventPayload,
+ long timeoutMillis,
+ @NonNull Callback callback) {
+ Log.d(TAG, "onDetect (Hardware trigger): " + eventPayload);
+ Trace.beginAsyncSection("HDS.onDetected", 0);
+
+ int sampleRate = eventPayload.getCaptureAudioFormat().getSampleRate();
+ int bytesPerSecond =
+ eventPayload.getCaptureAudioFormat().getFrameSizeInBytes() * sampleRate;
+
+ Integer captureSession = 0;
+ try {
+ Method getCaptureSessionMethod = eventPayload.getClass().getMethod("getCaptureSession");
+ captureSession = (Integer) getCaptureSessionMethod.invoke(eventPayload);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ int sessionId =
+// generateSessionId ?
+// AudioManager.AUDIO_SESSION_ID_GENERATE :
+ captureSession;
+ Trace.beginAsyncSection("HDS.createAudioRecord", 1);
+ AudioRecord record = createAudioRecord(eventPayload, bytesPerSecond, sessionId);
+ Trace.endAsyncSection("HDS.createAudioRecord", 1);
+ if (record.getState() != AudioRecord.STATE_INITIALIZED) {
+ Log.e(TAG, "Failed to init first AudioRecord.");
+ callback.onRejected(new HotwordRejectedResult.Builder().build());
+ return;
+ }
+
+ byte[] buffer = new byte[bytesPerSecond * (int) DSP_AUDIO_READ_DURATION.getSeconds()];
+ Log.d(TAG, "starting read: bytesPerSecond=" + bytesPerSecond
+ + ", totalBufferSize=" + buffer.length);
+ Trace.beginAsyncSection("HDS.startRecording", 1);
+ record.startRecording();
+ Trace.endAsyncSection("HDS.startRecording", 1);
+ Trace.beginAsyncSection("AudioUtils.read", 1);
+ AudioUtils.read(record, bytesPerSecond, DSP_AUDIO_READ_DURATION.getSeconds(), buffer);
+ Trace.endAsyncSection("AudioUtils.read", 1);
+
+ callback.onDetected(
+ new HotwordDetectedResult.Builder()
+ .setMediaSyncEvent(
+ record.shareAudioHistory("com.example.android.voiceinteractor", 0))
+ .setHotwordPhraseId(getKeyphraseId(eventPayload))
+ .build());
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ Log.i(TAG, "Releasing audio record");
+ record.stop();
+ record.release();
+ }, AUDIO_RECORD_RELEASE_TIMEOUT.toMillis());
+ Trace.endAsyncSection("HDS.onDetected", 0);
+ }
+
+ private int getKeyphraseId(AlwaysOnHotwordDetector.EventPayload payload) {
+ return 0;
+// if (payload.getKeyphraseRecognitionExtras().isEmpty()) {
+// return 0;
+// }
+// return payload.getKeyphraseRecognitionExtras().get(0).getKeyphraseId();
+ }
+
+ @Override
+ public void onDetect(@NonNull Callback callback) {
+ Log.w(TAG, "onDetect called for microphone trigger");
+ }
+
+}
diff --git a/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java
new file mode 100644
index 0000000..9e6b5b5
--- /dev/null
+++ b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVisualQueryDetectionService.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2023 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.voiceinteractor;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.Image;
+import android.media.ImageReader;
+import android.media.MediaRecorder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.PersistableBundle;
+import android.os.SharedMemory;
+import android.os.SystemClock;
+import android.service.voice.VisualQueryDetectionService;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.IntConsumer;
+
+
+/**
+ * Sample VisualQueryDetectionService that captures camera frame and sends back partial query.
+ */
+public class SampleVisualQueryDetectionService extends VisualQueryDetectionService {
+ static final String TAG = "SVisualQueryDetectionSrvc";
+
+ private final String FAKE_QUERY = "What is the weather today?";
+
+ // Camera module related variables
+ // Set this to different values for different modes
+ private final int CAPTURE_MODE = CameraDevice.TEMPLATE_RECORD;
+ private String mCameraId;
+ private CaptureRequest.Builder mCaptureRequestBuilder;
+ private CameraCaptureSession mCameraCaptureSession;
+ private CameraDevice mCameraDevice;
+ private ImageReader mImageReader;
+ private Handler mCameraBackgroundHandler;
+ private HandlerThread mCameraBackgroundThread;
+
+ // audio module related variables
+ private static final int AUDIO_SAMPLE_RATE_IN_HZ = 16000;
+ private static final int AUDIO_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
+ private static final int AUDIO_FORMAT = AudioFormat.ENCODING_DEFAULT;
+ private static final int BUFFER_SIZE = AUDIO_SAMPLE_RATE_IN_HZ;
+ private AudioRecord mAudioRecord;
+ private Handler mAudioBackgroundHandler;
+ private HandlerThread mAudioBackgroundThread;
+
+
+ @Override
+ public void onStartDetection() {
+ Log.i(TAG, "onStartDetection");
+ startBackgroundThread();
+ openCamera();
+ mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT,
+ AUDIO_SAMPLE_RATE_IN_HZ, AUDIO_CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE);
+ }
+
+ @Override
+ public void onStopDetection() {
+ Log.i(TAG, "onStopDetection");
+ releaseResources();
+ stopBackgroundThread();
+ }
+
+ @Override
+ public void onUpdateState(@Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory, long callbackTimeoutMillis,
+ @Nullable IntConsumer statusCallback) {
+ Log.i(TAG, "onUpdateState");
+ if (statusCallback != null) {
+ statusCallback.accept(0);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "Destroying visual query detection service");
+ onStopDetection();
+ }
+
+ /* Main logics of the system */
+ private void onReceiveImage(ImageReader reader){
+ Log.i(TAG, "Image received.");
+ Image image = null;
+ try {
+ image = reader.acquireLatestImage();
+ ByteBuffer buffer = image.getPlanes()[0].getBuffer();
+ byte[] bytes = new byte[buffer.capacity()];
+ buffer.get(bytes);
+ // Camera frame triggers attention
+ Log.i(TAG, "Image bytes received: " + Arrays.toString(bytes));
+ gainedAttention();
+ openMicrophone();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (image != null) {
+ image.close();
+ }
+ }
+ SystemClock.sleep(2_000); // wait 2 second to turn off attention
+ closeMicrophone();
+ lostAttention();
+ }
+
+ private void onReceiveAudio(){
+ try {
+ byte[] bytes = new byte[BUFFER_SIZE];
+ int result = mAudioRecord.read(bytes, 0, BUFFER_SIZE);
+ if (result != AudioRecord.ERROR_INVALID_OPERATION) {
+ // The buffer can be all zeros due to initialization and reading delay
+ Log.i(TAG, "Audio bytes received: " + Arrays.toString(bytes));
+ streamQuery(FAKE_QUERY);
+ finishQuery();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ SystemClock.sleep(2_000); //sleep so the buffer is a stable value
+ }
+
+ /* Sample Camera Module */
+ private void openCamera() {
+ CameraManager manager = getSystemService(CameraManager.class);
+ Log.i(TAG, "Attempting to open camera");
+ try {
+ mCameraId = manager.getCameraIdList()[0]; //get front facing camera
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId);
+ Size imageSize = characteristics.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+ .getOutputSizes(ImageFormat.JPEG)[0];
+ initializeImageReader(imageSize.getWidth(), imageSize.getHeight());
+ manager.openCamera(mCameraId, stateCallback, mCameraBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ Log.i(TAG, "Camera opened.");
+ }
+
+ private void initializeImageReader(int width, int height) {
+ // Initialize image reader
+ mImageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 2);
+ ImageReader.OnImageAvailableListener readerListener =
+ new ImageReader.OnImageAvailableListener() {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ onReceiveImage(reader);
+ }
+ } ;
+ mImageReader.setOnImageAvailableListener(readerListener, mCameraBackgroundHandler);
+ }
+
+ private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
+ @Override
+ public void onOpened(CameraDevice camera) {
+ // This is called when the camera is open
+ Log.i(TAG, "onCameraOpened");
+ mCameraDevice = camera;
+ createCameraPreview();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ mCameraDevice.close();
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ };
+
+ private void createCameraPreview() {
+ Range<Integer> fpsRange = new Range<>(1,2);
+ try {
+ mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CAPTURE_MODE);
+ Surface imageSurface = mImageReader.getSurface();
+ mCaptureRequestBuilder.addTarget(imageSurface);
+ mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+ mCameraDevice.createCaptureSession(List.of(imageSurface),
+ new CameraCaptureSession.StateCallback() {
+ @Override
+ public void onConfigured(
+ @NonNull CameraCaptureSession cameraCaptureSession) {
+ //The camera is already closed
+ if (mCameraDevice == null) {
+ return;
+ }
+ // When the session is ready, we start displaying the preview.
+ mCameraCaptureSession = cameraCaptureSession;
+ updatePreview();
+ Log.i(TAG, "Capture session configured.");
+ }
+
+ @Override
+ public void onConfigureFailed(
+ @NonNull CameraCaptureSession cameraCaptureSession) {
+ //No-op
+ }
+ }, null);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ Log.i(TAG, "Camera preview created.");
+ }
+
+ private void updatePreview() {
+ if (null == mCameraDevice) {
+ Log.e(TAG, "updatePreview error, return");
+ }
+ mCaptureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
+ try {
+ if (CAPTURE_MODE == CameraDevice.TEMPLATE_STILL_CAPTURE
+ || CAPTURE_MODE == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
+ mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), null,
+ mCameraBackgroundHandler);
+ } else if (CAPTURE_MODE == CameraDevice.TEMPLATE_RECORD
+ || CAPTURE_MODE == CameraDevice.TEMPLATE_PREVIEW){
+ mCameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null,
+ mCameraBackgroundHandler);
+ } else {
+ throw new IllegalStateException("Capture mode not supported.");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /* Sample Microphone Module */
+ private void openMicrophone() {
+ mAudioRecord.startRecording();
+ mAudioBackgroundHandler.post(this::onReceiveAudio);
+ }
+
+ private void closeMicrophone() {
+ if (mAudioRecord != null) {
+ mAudioRecord.stop();
+ }
+ }
+
+ private void releaseResources() {
+ mCameraId = null;
+ mCaptureRequestBuilder = null;
+ // Release mCameraCaptureSession
+ mCameraCaptureSession.close();
+ mCameraCaptureSession = null;
+ // Release mCameraDevice
+ mCameraDevice.close();
+ mCameraDevice = null;
+ // Release mImageReader
+ mImageReader.close();
+ mImageReader = null;
+ // Release mAudioRecord
+ mAudioRecord.release();
+ mAudioRecord = null;
+ }
+ // Handlers
+ private void startBackgroundThread() {
+ mCameraBackgroundThread = new HandlerThread("Camera Background Thread");
+ mCameraBackgroundThread.start();
+ mCameraBackgroundHandler = new Handler(mCameraBackgroundThread.getLooper());
+ mAudioBackgroundThread = new HandlerThread("Audio Background Thread");
+ mAudioBackgroundThread.start();
+ mAudioBackgroundHandler = new Handler(mAudioBackgroundThread.getLooper());
+ }
+
+ private void stopBackgroundThread() {
+ mCameraBackgroundThread.quitSafely();
+ try {
+ mCameraBackgroundThread.join();
+ mCameraBackgroundThread = null;
+ mCameraBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to stop camera thread.");
+ }
+ mAudioBackgroundThread.quitSafely();
+ try {
+ mAudioBackgroundThread.join();
+ mAudioBackgroundThread = null;
+ mAudioBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to stop audio thread.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java
new file mode 100644
index 0000000..8504ea1
--- /dev/null
+++ b/samples/VoiceInteractionService/src/com/example/android/voiceinteractor/SampleVoiceInteractionService.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2021 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.voiceinteractor;
+
+import static android.service.voice.AlwaysOnHotwordDetector.STATE_HARDWARE_UNAVAILABLE;
+import static android.service.voice.AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED;
+import static android.service.voice.AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Trace;
+import android.service.voice.AlwaysOnHotwordDetector;
+import android.service.voice.AlwaysOnHotwordDetector.EventPayload;
+import android.service.voice.HotwordDetector;
+import android.service.voice.HotwordRejectedResult;
+import android.service.voice.SandboxedDetectionInitializer;
+import android.service.voice.VisualQueryDetectionServiceFailure;
+import android.service.voice.VisualQueryDetector;
+import android.service.voice.VoiceInteractionService;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.time.Duration;
+import java.util.Locale;
+import java.util.concurrent.Executors;
+
+public class SampleVoiceInteractionService extends VoiceInteractionService {
+ public static final String DSP_MODEL_KEYPHRASE = "X Google";
+ private static final String TAG = "VIS";
+
+ // AudioRecord config
+ private static final Duration AUDIO_RECORD_BUFFER_DURATION = Duration.ofSeconds(5);
+ private static final Duration AUDIO_READ_DURATION = Duration.ofSeconds(3);
+
+ // DSP model config
+ private static final Locale DSP_MODEL_LOCALE = Locale.US;
+
+ private final IBinder binder = new LocalBinder();
+
+ HotwordDetector mHotwordDetector;
+ VisualQueryDetector mVisualQueryDetector;
+ Callback mHotwordDetectorCallback;
+ VisualQueryDetector.Callback mVisualQueryDetectorCallback;
+ Bundle mData = new Bundle();
+ AudioFormat mAudioFormat;
+ EventPayload mLastPayload;
+
+ private static AudioRecord createAudioRecord(EventPayload eventPayload, int bytesPerSecond) {
+ int audioRecordBufferSize = getBufferSizeInBytes(bytesPerSecond,
+ AUDIO_RECORD_BUFFER_DURATION.getSeconds());
+ Log.d(TAG, "creating AudioRecord: bytes=" + audioRecordBufferSize
+ + ", lengthSeconds=" + (audioRecordBufferSize / bytesPerSecond));
+ return new AudioRecord.Builder()
+ .setAudioAttributes(
+ new AudioAttributes.Builder()
+ .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD)
+ .build())
+ .setAudioFormat(eventPayload.getCaptureAudioFormat())
+ .setBufferSizeInBytes(audioRecordBufferSize)
+ .setSharedAudioEvent(eventPayload.getHotwordDetectedResult().getMediaSyncEvent())
+ .build();
+ }
+
+ private static int getBufferSizeInBytes(int bytesPerSecond, float bufferLengthSeconds) {
+ return (int) (bytesPerSecond * bufferLengthSeconds);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if ("local".equals(intent.getAction())) {
+ return binder;
+ }
+ return super.onBind(intent);
+ }
+
+ @Override
+ public void onReady() {
+ super.onReady();
+ Log.i(TAG, "onReady");
+ mHotwordDetectorCallback = new Callback();
+ mVisualQueryDetectorCallback = new VisualQueryDetectorCallback();
+ mHotwordDetector = createAlwaysOnHotwordDetector(DSP_MODEL_KEYPHRASE,
+ DSP_MODEL_LOCALE, null, null, mHotwordDetectorCallback);
+
+ }
+
+ @Override
+ public void onShutdown() {
+ super.onShutdown();
+ Log.i(TAG, "onShutdown");
+ }
+
+ public class LocalBinder extends Binder {
+ SampleVoiceInteractionService getService() {
+ // Return this instance of LocalService so clients can call public methods
+ return SampleVoiceInteractionService.this;
+ }
+ }
+
+ class VisualQueryDetectorCallback implements VisualQueryDetector.Callback {
+ @Override
+ public void onQueryDetected(@NonNull String partialQuery) {
+ Log.i(TAG, "VQD partial query detected: "+ partialQuery);
+ }
+
+ @Override
+ public void onQueryRejected() {
+ Log.i(TAG, "VQD query rejected");
+ }
+
+ @Override
+ public void onQueryFinished() {
+ Log.i(TAG, "VQD query finished");
+ }
+
+ @Override
+ public void onVisualQueryDetectionServiceInitialized(int status) {
+ Log.i(TAG, "VQD init: "+ status);
+ if (status == SandboxedDetectionInitializer.INITIALIZATION_STATUS_SUCCESS) {
+ mVisualQueryDetector.startRecognition();
+ }
+ }
+
+ @Override
+ public void onVisualQueryDetectionServiceRestarted() {
+ Log.i(TAG, "VQD restarted");
+ mVisualQueryDetector.startRecognition();
+ }
+
+ @Override
+ public void onFailure(
+ VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure) {
+ Log.i(TAG, "VQD onFailure visualQueryDetectionServiceFailure: "
+ + visualQueryDetectionServiceFailure);
+ }
+
+ @Override
+ public void onUnknownFailure(String errorMessage) {
+ Log.i(TAG, "VQD onUnknownFailure errorMessage: " + errorMessage);
+ }
+ };
+
+ class Callback extends AlwaysOnHotwordDetector.Callback {
+
+ private boolean mAvailable = false;
+
+ @Override
+ public void onAvailabilityChanged(int status) {
+ Log.i(TAG, "onAvailabilityChanged: " + status);
+ if (status == STATE_HARDWARE_UNAVAILABLE) {
+ // adb shell dumpsys package com.example.android.voiceinteractor | grep HOTWO
+ Log.w(
+ TAG,
+ "Hotword hardware unavailable. You may need to pre-grant "
+ + "CAPTURE_AUDIO_HOTWORD to this app, grant record audio to the app"
+ + "in settings, and/or change the keyphrase "
+ + "to one supported by the device's default assistant.");
+ }
+ if (status == STATE_KEYPHRASE_UNENROLLED) {
+ Intent enrollIntent = null;
+ enrollIntent = ((AlwaysOnHotwordDetector) mHotwordDetector).createEnrollIntent();
+ if (enrollIntent == null) {
+ Log.w(TAG, "No enroll intent found. Try enrolling the keyphrase using the"
+ + " device's default assistant.");
+ return;
+ }
+ ComponentName component = startForegroundService(enrollIntent);
+ Log.i(TAG, "Start enroll intent: " + component);
+ }
+ if (status == STATE_KEYPHRASE_ENROLLED) {
+ Log.i(TAG, "Keyphrase enrolled; ready to recognize.");
+ mAvailable = true;
+ }
+ }
+
+ @Override
+ public void onRejected(@NonNull HotwordRejectedResult result) {
+ mHotwordDetector.startRecognition();
+ }
+
+ @Override
+ public void onDetected(@NonNull EventPayload eventPayload) {
+ Trace.beginAsyncSection("VIS.onDetected", 0);
+ onDetected(eventPayload, false);
+ Trace.endAsyncSection("VIS.onDetected", 0);
+ }
+
+ public void onDetected(@NonNull EventPayload eventPayload, boolean generateSessionId) {
+ Log.i(TAG, "onDetected: " + eventPayload);
+ Log.i(TAG, "minBufferSize: "
+ + AudioRecord.getMinBufferSize(
+ eventPayload.getCaptureAudioFormat().getSampleRate(),
+ eventPayload.getCaptureAudioFormat().getChannelMask(),
+ eventPayload.getCaptureAudioFormat().getEncoding()));
+
+ int sampleRate = eventPayload.getCaptureAudioFormat().getSampleRate();
+ int bytesPerSecond =
+ eventPayload.getCaptureAudioFormat().getFrameSizeInBytes() * sampleRate;
+
+ Trace.beginAsyncSection("VIS.createAudioRecord", 1);
+
+ // For Non-trusted:
+// Integer captureSession = 0;
+// try {
+// Method getCaptureSessionMethod = eventPayload.getClass().getMethod("getCaptureSession");
+// captureSession = (Integer) getCaptureSessionMethod.invoke(eventPayload);
+// } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+// e.printStackTrace();
+// }
+// int sessionId = generateSessionId ?
+// AudioManager.AUDIO_SESSION_ID_GENERATE : captureSession;
+// AudioRecord record = createAudioRecord(eventPayload, bytesPerSecond, sessionId);
+
+ AudioRecord record = createAudioRecord(eventPayload, bytesPerSecond);
+ Trace.endAsyncSection("VIS.createAudioRecord", 1);
+ if (record.getState() != AudioRecord.STATE_INITIALIZED) {
+ Trace.setCounter("VIS AudioRecord.getState",
+ record.getState());
+ Log.e(TAG, "Failed to init first AudioRecord.");
+ mHotwordDetector.startRecognition();
+ return;
+ }
+
+ byte[] buffer = new byte[bytesPerSecond * (int) AUDIO_READ_DURATION.getSeconds()];
+ Trace.beginAsyncSection("VIS.startRecording", 1);
+ record.startRecording();
+ Trace.endAsyncSection("VIS.startRecording", 1);
+ Trace.beginAsyncSection("AudioUtils.read", 1);
+ int numBytes = AudioUtils.read(record, bytesPerSecond, AUDIO_READ_DURATION.getSeconds(),
+ buffer);
+ Trace.endAsyncSection("AudioUtils.read", 1);
+
+// try {
+// Thread.sleep(2000);
+// } catch (InterruptedException e) {
+// Thread.interrupted();
+// throw new RuntimeException(e);
+// }
+
+
+ record.stop();
+ record.release();
+
+ Log.i(TAG, "numBytes=" + numBytes + " audioSeconds=" + numBytes * 1.0 / bytesPerSecond);
+ mData.putByteArray("1", buffer);
+ mAudioFormat = eventPayload.getCaptureAudioFormat();
+ mLastPayload = eventPayload;
+ mHotwordDetector.startRecognition();
+ }
+
+ @Override
+ public void onError() {
+ Log.i(TAG, "onError");
+ mHotwordDetector.startRecognition();
+ }
+
+ @Override
+ public void onRecognitionPaused() {
+ Log.i(TAG, "onRecognitionPaused");
+ }
+
+ @Override
+ public void onRecognitionResumed() {
+ Log.i(TAG, "onRecognitionResumed");
+ }
+
+ @Override
+ public void onHotwordDetectionServiceInitialized(int status) {
+ Log.i(TAG, "onHotwordDetectionServiceInitialized: " + status
+ + ". mAvailable=" + mAvailable);
+ if (mAvailable) {
+ mHotwordDetector.startRecognition();
+ }
+ //TODO(b/265535257): Provide two services independent lifecycle.
+ mVisualQueryDetector = createVisualQueryDetector(null, null,
+ Executors.newSingleThreadExecutor(), mVisualQueryDetectorCallback);
+ }
+ }
+}
diff --git a/sdk/build_tools_source.prop_template b/sdk/build_tools_source.prop_template
index d2dad00..4fb49e3 100644
--- a/sdk/build_tools_source.prop_template
+++ b/sdk/build_tools_source.prop_template
@@ -1,3 +1,3 @@
Pkg.UserSrc=false
-Pkg.Revision=${PLATFORM_SDK_VERSION}.0.2
-#Pkg.Revision=33.0.0 rc4
+Pkg.Revision=${PLATFORM_SDK_VERSION}.0.0
+#Pkg.Revision=34.0.0 rc3
diff --git a/sdk/platform_source.prop_template b/sdk/platform_source.prop_template
index 34766b7..fa996c7 100644
--- a/sdk/platform_source.prop_template
+++ b/sdk/platform_source.prop_template
@@ -2,7 +2,7 @@
Pkg.UserSrc=false
Platform.Version=${PLATFORM_VERSION}
Platform.CodeName=
-Pkg.Revision=2
+Pkg.Revision=1
AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
AndroidVersion.ExtensionLevel=${PLATFORM_SDK_EXTENSION_VERSION}
diff --git a/tools/winscope/.babelrc b/tools/winscope/.babelrc
deleted file mode 100644
index cedf24f..0000000
--- a/tools/winscope/.babelrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "presets": [
- "@babel/preset-env"
- ]
-}
\ No newline at end of file
diff --git a/tools/winscope/.eslintrc.js b/tools/winscope/.eslintrc.js
new file mode 100644
index 0000000..8edd759
--- /dev/null
+++ b/tools/winscope/.eslintrc.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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 = {
+ extends: ['eslint-config-prettier', 'eslint:recommended'],
+ plugins: ['eslint-plugin-prettier', '@typescript-eslint'],
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ },
+ env: {
+ es2022: true,
+ node: true,
+ browser: true,
+ webextensions: true,
+ jasmine: true,
+ protractor: true,
+ },
+ rules: {
+ 'no-unused-vars': 'off', // not very robust rule
+
+ // Partially taken from https://github.com/google/eslint-config-google
+ // Omitted layout & formatting rules because that's handled by prettier
+ 'no-var': 'error',
+ 'prefer-const': ['error', {destructuring: 'all'}],
+ 'prefer-rest-params': 'error',
+ 'prefer-spread': 'error',
+ },
+ globals: {
+ // Specify NodeJS global as temporary workaround for eslint bug:
+ // https://stackoverflow.com/questions/64089216/after-upgrade-eslint-says-nodejs-is-undefined
+ // https://github.com/Chatie/eslint-config/issues/45
+ NodeJS: true,
+ },
+};
diff --git a/tools/winscope/.eslintrc.json b/tools/winscope/.eslintrc.json
deleted file mode 100644
index b5271d1..0000000
--- a/tools/winscope/.eslintrc.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "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 75eede8..74f32dd 100644
--- a/tools/winscope/.gitignore
+++ b/tools/winscope/.gitignore
@@ -1,8 +1,44 @@
-node_modules/
-adb_proxy/venv/
-.vscode/
-dist/
-kotlin_build/
-yarn-error.log
-kotlin_build/
-.eslintcache
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# Compiled output
+/dist
+/tmp
+/out-tsc
+/bazel-out
+
+# Node
+/node_modules
+npm-debug.log
+
+# Kotlin transpiled code
+/kotlin_build
+
+# IDEs and editors
+.idea/
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# Visual Studio Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# Miscellaneous
+/.angular/cache
+.sass-cache/
+/connect.lock
+/coverage
+/libpeerconnection.log
+testem.log
+/typings
+
+# System files
+.DS_Store
+Thumbs.db
diff --git a/tools/winscope/OWNERS b/tools/winscope/OWNERS
new file mode 100644
index 0000000..9cb826f
--- /dev/null
+++ b/tools/winscope/OWNERS
@@ -0,0 +1,5 @@
+natanieljr@google.com
+pablogamito@google.com
+keanmariotti@google.com
+jjaggi@google.com
+roosa@google.com
diff --git a/tools/winscope/README.md b/tools/winscope/README.md
index f34c022..a2877b2 100644
--- a/tools/winscope/README.md
+++ b/tools/winscope/README.md
@@ -8,18 +8,17 @@
contain the proto definitions for their internal states.
### Checking out code and setting up environment
-* Install [Yarn](https://yarnpkg.com), a JS package manager
* [Download Android source](https://source.android.com/setup/build/downloading)
* Navigate to `development/tools/winscope`
-* Run `yarn install`
+* Run `npm install`
-### Building & testing changes
+### Build & test & deploy changes
* Navigate to `development/tools/winscope`
-* Run `yarn run dev`
+* Run `npm run` to get the list of available commands
### Update IntDefMapping
* Build `framework-minus-apex-intdefs` module and a preprocessor will
-generate the latest IntDefMapping. From the `ANDROID_ROOT` run:
+ generate the latest IntDefMapping. From the `ANDROID_ROOT` run:
```
. build/envsetup.sh
m framework-minus-apex-intdefs
diff --git a/tools/winscope/all b/tools/winscope/all
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/winscope/all
diff --git a/tools/winscope/env/dev.env.js b/tools/winscope/env/dev.env.js
deleted file mode 100644
index 0b533b2..0000000
--- a/tools/winscope/env/dev.env.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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
deleted file mode 100644
index f1bbba0..0000000
--- a/tools/winscope/env/prod.env.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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/google.tslint.json b/tools/winscope/google.tslint.json
new file mode 100644
index 0000000..68500bd
--- /dev/null
+++ b/tools/winscope/google.tslint.json
@@ -0,0 +1,138 @@
+// Rules that don't require type information, Lint category.
+// Enable non-strict mode to allow comments.
+{
+ "rules": {
+ // The disabled rules below are google3 custom rules that need to be built
+ // and added to the project (see google3/javascript/typescript/tslint/rules/).
+ // If needed, it should be possible to build and run them in the Winscope npm
+ // environment as well. It is probably simpler and quicker to wait till
+ // Winscope is ported to google3 though.
+
+ //"angular-use-component-harnesses": true,
+ //"angular-output-is-readonly": true,
+ "array-type": [true, "array-simple"],
+ "arrow-return-shorthand": true,
+ "ban": [true,
+ ["fit"],
+ ["fpit"],
+ ["fdescribe"],
+ ["fpdescribe"],
+ ["xit"],
+ ["xpit"],
+ ["xdescribe"],
+ ["xpdescribe"],
+ ["fitAsync"],
+ ["xitAsync"],
+ ["fitFakeAsync"],
+ ["xitFakeAsync"],
+ {"name": ["it", "skip"]},
+ {"name": ["it", "only"]},
+ {"name": ["it", "async", "skip"]},
+ {"name": ["it", "async", "only"]},
+ {"name": ["pit", "skip"]},
+ {"name": ["pit", "only"]},
+ {"name": ["pit", "async", "skip"]},
+ {"name": ["pit", "async", "only"]},
+ {"name": ["describe", "skip"]},
+ {"name": ["describe", "only"]},
+ {"name": ["pdescribe", "skip"]},
+ {"name": ["pdescribe", "only"]},
+ {"name": ["describeWithDate", "skip"]},
+ {"name": ["describeWithDate", "only"]},
+ {"name": "parseInt", "message": "See http://go/tsstyle#type-coercion"},
+ {"name": "parseFloat", "message": "See http://go/tsstyle#type-coercion"},
+ {"name": "Array", "message": "See http://go/tsstyle#array-constructor"},
+ {"name": ["*", "innerText"], "message": "Use .textContent instead. http://go/typescript/patterns#browser-oddities"},
+ {"name": ["goog", "setTestOnly"], "message": "See http://go/tsstyle#tests"}
+ ],
+ //"ban-as-never": true,
+ //"ban-implicit-undefined-default-parameters": true,
+ //"ban-jsdoc-enum-tag": true,
+ //"ban-malformed-import-paths": true,
+ //"ban-passing-async-function-to-describe": true,
+ //"ban-spy-returning-rejected-promise": true,
+ //"ban-strict-prop-init-comment": true,
+ // allowedSuppressions is a list of strings with no whitespace which, when
+ // found wrapped in parentheses immediately after the suppresion, will
+ // prevent this rule from triggering.
+ // For example: `// @ts-ignore(go/ts99upgrade) Some explanation.`
+ // Prefer using b/ bug links or go/ go links.
+ // To check if your suppression string is available in prod, use:
+ // cl-status/#/summary/tricorder.go-worker/[[SUBMITTED_CL_NUM]]
+ // Or, for CLs using the suppression, a go/startblock directive of:
+ // cl-status tricorder.go-worker contains cl/[[SUBMITTED_CL_NUM]] in prod
+ //"ban-ts-suppressions": [true, {
+ // "allowedSuppressions": [
+ // "b/249999919", // Node 18.x typings update
+ // "go/tsjs-aatm",
+ // "go/ts49upgrade",
+ // "go/jspb-ts-enums-fix",
+ // "KEEP_ME_LAST_TO_AVOID_NEEDING_TO_ADD_A_COMMA_TO_THE_LAST_ENTRY"
+ // ]
+ //}],
+ //"ban-tslint-disable": true,
+ "ban-types": [true,
+ ["Object", "Use {} or 'object' instead. See http://go/ts-style#wrapper-types"],
+ ["String", "Use 'string' instead."],
+ ["Number", "Use 'number' instead."],
+ ["Boolean", "Use 'boolean' instead."],
+ // Add tests in google3/javascript/typescript/tslint/test/googleConfig/ban_types.ts.lint
+ ["AnyDuring(?!((ICentral|CelloJs|AngularIvy|Drive|1TF|AllAsUnknown|GoogPromiseThen|Search|DWE|JasmineApril2021|Assisted)Migration)).*",
+ "AnyDuringMigration is a quick-fix used during TypeScript migrations, and should be removed as soon as possible. See http://go/any_during_migration."]
+ ],
+ // go/keep-sorted start
+ //"class-as-namespace": true,
+ "class-name": true,
+ "curly": [true, "ignore-same-line"],
+ //"decorator-placement": true,
+ //"discourage-angular-material-subpackage-imports": true,
+ //"enforce-comments-on-exported-symbols": true,
+ //"enforce-name-casing": true,
+ //"file-comment": true,
+ //"fix-trailing-comma-import-export": true,
+ "forin": true,
+ "interface-name": [true, "never-prefix"],
+ "interface-over-type-literal": true,
+ "jsdoc-format": true,
+ //"jsdoc-tags": true,
+ "label-position": true,
+ "member-access": [true, "no-public"],
+ "new-parens": true,
+ "no-angle-bracket-type-assertion": true,
+ //TODO (b/264508345): enable rule below after removeing 'any' types
+ //"no-any": true,
+ "no-conditional-assignment": [true, "allow-within-parenthesis"],
+ "no-construct": true,
+ "no-debugger": true,
+ "no-default-export": true,
+ "no-duplicate-switch-case": true,
+ //"no-inferrable-new-expression": true,
+ "no-namespace": [true, "allow-declarations"],
+ //"no-new-decorators": true,
+ //"no-quoted-property-signatures": true,
+ "no-reference": true,
+ "no-require-imports": true,
+ //"no-return-only-generics": true,
+ "no-string-throw": true,
+ //"no-undefined-type-alias": true,
+ //"no-unnecessary-escapes": true,
+ "no-unsafe-finally": true,
+ "no-unused-expression": [true, "allow-fast-null-checks"],
+ "no-unused-variable": true,
+ //"no-unused-wiz-injections": true,
+ "no-var-keyword": true,
+ "object-literal-shorthand": true,
+ "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"],
+ "prefer-const": [true, {"destructuring": "all"}],
+ //"prefer-function-declaration": true,
+ //"prefer-type-annotation": true,
+ "radix": true,
+ "semicolon": [true, "always", "strict-bound-class-methods"],
+ "static-this": true,
+ "switch-default": true,
+ "triple-equals": [true, "allow-null-check"],
+ "unnecessary-constructor": true
+ //"well-formed-closure-message": true
+ // go/keep-sorted end
+ }
+}
diff --git a/tools/winscope/hooks/pre-upload b/tools/winscope/hooks/pre-upload
new file mode 100755
index 0000000..bd9c1be
--- /dev/null
+++ b/tools/winscope/hooks/pre-upload
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+WINSCOPE_SRC_PATTERN="tools/winscope/"
+
+match=false
+for file in "$@"
+do
+ if echo $file | grep --quiet "$WINSCOPE_SRC_PATTERN"
+ then
+ match=true
+ fi
+done
+
+# If there are changes to winscope files and npm is installed
+if $match && (which node > /dev/null)
+then
+ echo "Running winscope presubmit tests..."
+ npm run test:presubmit --prefix $WINSCOPE_SRC_PATTERN
+fi
diff --git a/tools/winscope/karma.conf.js b/tools/winscope/karma.conf.js
new file mode 100644
index 0000000..bd03ace
--- /dev/null
+++ b/tools/winscope/karma.conf.js
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 webpackConfig = require('./webpack.config.common');
+delete webpackConfig.entry;
+delete webpackConfig.output;
+
+module.exports = (config) => {
+ config.set({
+ frameworks: ['jasmine', 'webpack'],
+ plugins: ['karma-webpack', 'karma-chrome-launcher', 'karma-jasmine', 'karma-sourcemap-loader'],
+ files: [{pattern: 'src/main_component_test.ts', watched: false}],
+ preprocessors: {
+ 'src/main_component_test.ts': ['webpack', 'sourcemap'],
+ },
+ singleRun: true,
+ browsers: ['ChromeHeadless'],
+ webpack: webpackConfig,
+ });
+};
diff --git a/tools/winscope/libs/virtualList/Item.js b/tools/winscope/libs/virtualList/Item.js
deleted file mode 100644
index 2f1a0e5..0000000
--- a/tools/winscope/libs/virtualList/Item.js
+++ /dev/null
@@ -1,81 +0,0 @@
-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
deleted file mode 100644
index 0b99a70..0000000
--- a/tools/winscope/libs/virtualList/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# 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
deleted file mode 100644
index 1a6b632..0000000
--- a/tools/winscope/libs/virtualList/VirtualList.js
+++ /dev/null
@@ -1,354 +0,0 @@
-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
deleted file mode 100644
index 410b581..0000000
--- a/tools/winscope/libs/virtualList/props.js
+++ /dev/null
@@ -1,150 +0,0 @@
-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
deleted file mode 100644
index 94688f5..0000000
--- a/tools/winscope/libs/virtualList/virtual.js
+++ /dev/null
@@ -1,305 +0,0 @@
-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/loaders/proto-loader.js b/tools/winscope/loaders/proto-loader.js
index a8e5058..833384d 100644
--- a/tools/winscope/loaders/proto-loader.js
+++ b/tools/winscope/loaders/proto-loader.js
@@ -1,11 +1,11 @@
/*
- * Copyright 2017, The Android Open Source Project
+ * Copyright (C) 2022 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
+ * 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,
@@ -13,47 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-var path = require("path");
-var fs = require('fs');
-var protobuf = require('protobufjs');
-var loaderUtils = require('loader-utils');
-var jsonTarget = require('protobufjs/cli/targets/json');
-
+const fs = require('fs');
+const protobuf = require('protobufjs');
+const loaderUtils = require('loader-utils');
module.exports = function(source) {
- var self = this;
+ const webpackContext = this;
+ const root = new protobuf.Root();
+ const paths = loaderUtils.getOptions(this)['paths'] || [];
- var root = new protobuf.Root();
+ root.resolvePath = function resolvePath(origin, target) {
+ const normOrigin = protobuf.util.path.normalize(origin);
+ const normTarget = protobuf.util.path.normalize(target);
- var paths = loaderUtils.getOptions(this)['paths'] || [];
+ let candidates = [
+ protobuf.util.path.resolve(normOrigin, normTarget, true)
+ ];
+ candidates = candidates.concat(
+ paths.map(path => protobuf.util.path.resolve(path + "/", target))
+ );
- // Search include paths when resolving imports
- root.resolvePath = function pbjsResolvePath(origin, target) {
- var normOrigin = protobuf.util.path.normalize(origin);
- var normTarget = protobuf.util.path.normalize(target);
-
- var resolved = protobuf.util.path.resolve(normOrigin, normTarget, true);
- if (fs.existsSync(resolved)) {
- self.addDependency(resolved);
- return resolved;
- }
-
- for (var i = 0; i < paths.length; ++i) {
- var iresolved = protobuf.util.path.resolve(paths[i] + "/", target);
- if (fs.existsSync(iresolved)) {
- self.addDependency(iresolved);
- return iresolved;
+ for (const path of candidates) {
+ if (fs.existsSync(path)) {
+ webpackContext.addDependency(path);
+ return path;
}
}
- self.addDependency(resolved);
- return resolved;
+ throw Error(`Failed to resolve path: origin=${origin}, target=${target}, candidates=${candidates}`);
};
- root.loadSync(self.resourcePath).resolveAll();
+ root.loadSync(webpackContext.resourcePath).resolveAll();
- var result = JSON.stringify(root, null, 2);
+ const result = JSON.stringify(root, null, 2);
return `module.exports = ${result}`;
};
diff --git a/tools/winscope/package-lock.json b/tools/winscope/package-lock.json
new file mode 100644
index 0000000..e041160
--- /dev/null
+++ b/tools/winscope/package-lock.json
@@ -0,0 +1,30228 @@
+{
+ "name": "winscope",
+ "version": "0.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "winscope",
+ "version": "0.0.0",
+ "dependencies": {
+ "@angular/animations": "^14.0.0",
+ "@angular/cdk": "^14.2.6",
+ "@angular/common": "^14.0.0",
+ "@angular/compiler": "^14.0.0",
+ "@angular/core": "^14.0.1",
+ "@angular/elements": "^14.0.1",
+ "@angular/forms": "^14.0.0",
+ "@angular/material": "^14.2.6",
+ "@angular/platform-browser": "^14.0.0",
+ "@angular/platform-browser-dynamic": "^14.0.0",
+ "@angular/router": "^14.0.0",
+ "@auth0/auth0-angular": "^1.10.0",
+ "@ngrx/effects": "^14.0.2",
+ "@ngrx/store": "^14.0.2",
+ "@ngxs/store": "^3.7.4",
+ "auth0": "^3.0.1",
+ "dateformat": "^5.0.3",
+ "gl-matrix": "^3.4.3",
+ "html-loader": "^3.1.0",
+ "html-webpack-inline-source-plugin": "^1.0.0-beta.2",
+ "html-webpack-plugin": "^5.5.0",
+ "html2canvas": "^1.4.1",
+ "jsbn": "^1.1.0",
+ "jsbn-rsa": "^1.0.4",
+ "kotlin": "^1.8.10",
+ "kotlin-compiler": "^1.8.10",
+ "protobufjs": "^6.11.3",
+ "rxjs": "~7.5.0",
+ "style-loader": "^3.3.1",
+ "three": "^0.143.0",
+ "ts-loader": "^9.3.0",
+ "tslib": "^2.3.0",
+ "typescript": "~4.7.2",
+ "webgl-utils": "^1.0.1",
+ "webgl-utils.js": "^1.1.0",
+ "webpack-cli": "^4.10.0",
+ "zone.js": "~0.11.4"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "^14.0.0",
+ "@angular/cli": "~14.0.0",
+ "@angular/compiler-cli": "^14.0.0",
+ "@ngxs/devtools-plugin": "^3.7.4",
+ "@types/chrome": "^0.0.204",
+ "@types/dateformat": "^5.0.0",
+ "@types/jasmine": "~4.3.1",
+ "@types/jquery": "^3.5.14",
+ "@types/jsbn": "^1.2.30",
+ "@types/node": "^18.0.4",
+ "@types/three": "^0.143.0",
+ "@types/w3c-web-usb": "^1.0.6",
+ "@typescript-eslint/eslint-plugin": "^5.30.6",
+ "@typescript-eslint/parser": "^5.30.6",
+ "angular2-template-loader": "^0.6.2",
+ "eslint": "^8.19.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "jasmine": "~4.3.0",
+ "jasmine-core": "~4.1.0",
+ "karma": "~6.3.0",
+ "karma-chrome-launcher": "~3.1.0",
+ "karma-jasmine": "~5.0.0",
+ "karma-sourcemap-loader": "^0.3.8",
+ "karma-webpack": "^5.0.0",
+ "loader-utils": "^2.0.0",
+ "madge": "^5.0.1",
+ "prettier": "^2.8.1",
+ "prettier-plugin-organize-imports": "^3.2.1",
+ "protractor": "^7.0.0",
+ "sass": "^1.56.1",
+ "sass-loader": "^13.1.0",
+ "tslint": "^6.1.3",
+ "webpack": "^5.74.0"
+ }
+ },
+ "node_modules/@adobe/css-tools": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz",
+ "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==",
+ "dev": true
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+ "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect": {
+ "version": "0.1402.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.10.tgz",
+ "integrity": "sha512-/6YmPrgataj1jD2Uqd1ED+CG4DaZGacoeZd/89hH7hF76Nno8K18DrSOqJAEmDnOWegpSRGVLd0qP09IHmaG5w==",
+ "dev": true,
+ "dependencies": {
+ "@angular-devkit/core": "14.2.10",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/build-angular": {
+ "version": "14.2.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.10.tgz",
+ "integrity": "sha512-VCeZAyq4uPCJukKInaSiD4i/GgxgcU4jFlLFQtoYNmaBS4xbPOymL19forRIihiV0dwNEa2L694vRTAPMBxIfw==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "2.2.0",
+ "@angular-devkit/architect": "0.1402.10",
+ "@angular-devkit/build-webpack": "0.1402.10",
+ "@angular-devkit/core": "14.2.10",
+ "@babel/core": "7.18.10",
+ "@babel/generator": "7.18.12",
+ "@babel/helper-annotate-as-pure": "7.18.6",
+ "@babel/plugin-proposal-async-generator-functions": "7.18.10",
+ "@babel/plugin-transform-async-to-generator": "7.18.6",
+ "@babel/plugin-transform-runtime": "7.18.10",
+ "@babel/preset-env": "7.18.10",
+ "@babel/runtime": "7.18.9",
+ "@babel/template": "7.18.10",
+ "@discoveryjs/json-ext": "0.5.7",
+ "@ngtools/webpack": "14.2.10",
+ "ansi-colors": "4.1.3",
+ "babel-loader": "8.2.5",
+ "babel-plugin-istanbul": "6.1.1",
+ "browserslist": "^4.9.1",
+ "cacache": "16.1.2",
+ "copy-webpack-plugin": "11.0.0",
+ "critters": "0.0.16",
+ "css-loader": "6.7.1",
+ "esbuild-wasm": "0.15.5",
+ "glob": "8.0.3",
+ "https-proxy-agent": "5.0.1",
+ "inquirer": "8.2.4",
+ "jsonc-parser": "3.1.0",
+ "karma-source-map-support": "1.4.0",
+ "less": "4.1.3",
+ "less-loader": "11.0.0",
+ "license-webpack-plugin": "4.0.2",
+ "loader-utils": "3.2.1",
+ "mini-css-extract-plugin": "2.6.1",
+ "minimatch": "5.1.0",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "parse5-html-rewriting-stream": "6.0.1",
+ "piscina": "3.2.0",
+ "postcss": "8.4.16",
+ "postcss-import": "15.0.0",
+ "postcss-loader": "7.0.1",
+ "postcss-preset-env": "7.8.0",
+ "regenerator-runtime": "0.13.9",
+ "resolve-url-loader": "5.0.0",
+ "rxjs": "6.6.7",
+ "sass": "1.54.4",
+ "sass-loader": "13.0.2",
+ "semver": "7.3.7",
+ "source-map-loader": "4.0.0",
+ "source-map-support": "0.5.21",
+ "stylus": "0.59.0",
+ "stylus-loader": "7.0.0",
+ "terser": "5.14.2",
+ "text-table": "0.2.0",
+ "tree-kill": "1.2.2",
+ "tslib": "2.4.0",
+ "webpack": "5.74.0",
+ "webpack-dev-middleware": "5.3.3",
+ "webpack-dev-server": "4.11.0",
+ "webpack-merge": "5.8.0",
+ "webpack-subresource-integrity": "5.1.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "optionalDependencies": {
+ "esbuild": "0.15.5"
+ },
+ "peerDependencies": {
+ "@angular/compiler-cli": "^14.0.0",
+ "@angular/localize": "^14.0.0",
+ "@angular/service-worker": "^14.0.0",
+ "karma": "^6.3.0",
+ "ng-packagr": "^14.0.0",
+ "protractor": "^7.0.0",
+ "tailwindcss": "^2.0.0 || ^3.0.0",
+ "typescript": ">=4.6.2 <4.9"
+ },
+ "peerDependenciesMeta": {
+ "@angular/localize": {
+ "optional": true
+ },
+ "@angular/service-worker": {
+ "optional": true
+ },
+ "karma": {
+ "optional": true
+ },
+ "ng-packagr": {
+ "optional": true
+ },
+ "protractor": {
+ "optional": true
+ },
+ "tailwindcss": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/loader-utils": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
+ "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/sass": {
+ "version": "1.54.4",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz",
+ "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/sass-loader": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz",
+ "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==",
+ "dev": true,
+ "dependencies": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "fibers": ">= 3.1.0",
+ "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
+ "sass": "^1.3.0",
+ "sass-embedded": "*",
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "fibers": {
+ "optional": true
+ },
+ "node-sass": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/webpack": {
+ "version": "5.74.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
+ "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/wasm-edit": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.7.6",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.10.0",
+ "es-module-lexer": "^0.9.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.1.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.1.3",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/build-webpack": {
+ "version": "0.1402.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.10.tgz",
+ "integrity": "sha512-h+2MaSY7QSvoJ3R+Hvin21jVCfPGOTLdASIUk4Jmq6J3y5BSku3KSSaV8dWoBOBkFCwQyPQMRjiHoHKLpC1K7g==",
+ "dev": true,
+ "dependencies": {
+ "@angular-devkit/architect": "0.1402.10",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "webpack": "^5.30.0",
+ "webpack-dev-server": "^4.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-webpack/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/core": {
+ "version": "14.2.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.10.tgz",
+ "integrity": "sha512-K4AO7mROTdbhQ7chtyQd6oPwmuL+BPUh+wn6Aq1qrmYJK4UZYFOPp8fi/Ehs8meCEeywtrssOPfrOE4Gsre9dg==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.1.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.4"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.5.2"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/core/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/core/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/schematics": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.0.7.tgz",
+ "integrity": "sha512-nJUJXCBQr7rmVn6IXFAXMCWAB1w6JQmFGuFVW0G3GH/A0e+A3ttzJc6qVLYluqaFoafw394cZu24YJo55E/+Zg==",
+ "dev": true,
+ "dependencies": {
+ "@angular-devkit/core": "14.0.7",
+ "jsonc-parser": "3.0.0",
+ "magic-string": "0.26.1",
+ "ora": "5.4.1",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz",
+ "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.0.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.5.2"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/jsonc-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
+ "dev": true
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@angular/animations": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.12.tgz",
+ "integrity": "sha512-gwdnFZkvVUr+enUNfhfCGRGGqNHn1+vTA81apLfHYhJxgjiLUtETc4KTOrQevtDm022pEd+LSrvr8r+7ag+jkw==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "14.2.12"
+ }
+ },
+ "node_modules/@angular/cdk": {
+ "version": "14.2.7",
+ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz",
+ "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "optionalDependencies": {
+ "parse5": "^5.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^14.0.0 || ^15.0.0",
+ "@angular/core": "^14.0.0 || ^15.0.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/cli": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.0.7.tgz",
+ "integrity": "sha512-tABt1EDwBHm0ngsutdkXXWgPgHzapGLC7rSPHXStMc24ngViFZpXGzBCpompjHvXNt6bjklmJmuRvjS6+ktBZA==",
+ "dev": true,
+ "dependencies": {
+ "@angular-devkit/architect": "0.1400.7",
+ "@angular-devkit/core": "14.0.7",
+ "@angular-devkit/schematics": "14.0.7",
+ "@schematics/angular": "14.0.7",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ansi-colors": "4.1.1",
+ "debug": "4.3.4",
+ "ini": "3.0.0",
+ "inquirer": "8.2.4",
+ "jsonc-parser": "3.0.0",
+ "npm-package-arg": "9.0.2",
+ "npm-pick-manifest": "7.0.1",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "pacote": "13.3.0",
+ "resolve": "1.22.0",
+ "semver": "7.3.7",
+ "symbol-observable": "4.0.0",
+ "uuid": "8.3.2",
+ "yargs": "17.4.1"
+ },
+ "bin": {
+ "ng": "bin/ng.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular/cli/node_modules/@angular-devkit/architect": {
+ "version": "0.1400.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1400.7.tgz",
+ "integrity": "sha512-8dv/Ql86dHajsHYjjr5jvpiV7uXWbt7Mz4K/rGiOi+zzDNKPcZcuCejulWhOySDcCPjT/an47Qcwr+awL4Wr4g==",
+ "dev": true,
+ "dependencies": {
+ "@angular-devkit/core": "14.0.7",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular/cli/node_modules/@angular-devkit/core": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz",
+ "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.0.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.5.2"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/cli/node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@angular/cli/node_modules/jsonc-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
+ "dev": true
+ },
+ "node_modules/@angular/cli/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular/cli/node_modules/source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@angular/cli/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@angular/common": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.12.tgz",
+ "integrity": "sha512-oZunh9wfInFWhNO1P8uoEs/o4u8kerKMhw8GruywKm1TV7gHDP2Fi5WHGjFqq3XYptgBTPCTSEfyLX6Cwq1PUw==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "14.2.12",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/compiler": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.12.tgz",
+ "integrity": "sha512-u2MH9+NRwbbFDRNiPWPexed9CnCq9+pGHLuyACSP2uR6Ik68cE6cayeZbIeoEV5vWpda/XsLmJgPJysw7dAZLQ==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "14.2.12"
+ },
+ "peerDependenciesMeta": {
+ "@angular/core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/compiler-cli": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.12.tgz",
+ "integrity": "sha512-9Gkb9KFkaQPz8XaS8ZwwTioRZ4ywykdAWyceICEi78/Y9ConYrTX2SbFogzI2dPUZU8a04tMlbqTSmHjVbJftQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.17.2",
+ "chokidar": "^3.0.0",
+ "convert-source-map": "^1.5.1",
+ "dependency-graph": "^0.11.0",
+ "magic-string": "^0.26.0",
+ "reflect-metadata": "^0.1.2",
+ "semver": "^7.0.0",
+ "sourcemap-codec": "^1.4.8",
+ "tslib": "^2.3.0",
+ "yargs": "^17.2.1"
+ },
+ "bin": {
+ "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
+ "ngc": "bundles/src/bin/ngc.js",
+ "ngcc": "bundles/ngcc/main-ngcc.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler": "14.2.12",
+ "typescript": ">=4.6.2 <4.9"
+ }
+ },
+ "node_modules/@angular/core": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.12.tgz",
+ "integrity": "sha512-sGQxU5u4uawwvJa6jOTmGoisJiQ5HIN/RoBw99CmoqZIVyUSg9IRJJC1KVdH8gbpWBNLkElZv21lwJTL/msWyg==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "rxjs": "^6.5.3 || ^7.4.0",
+ "zone.js": "~0.11.4 || ~0.12.0"
+ }
+ },
+ "node_modules/@angular/elements": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-14.2.12.tgz",
+ "integrity": "sha512-FSAM5wz/Vi0DZZ9do4tuo1m8XyioBH9htsv1E2vStr6NXaq13U6TImQs9RcLIAOoKFV+pSuxSqHpT/Wfs9G30Q==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "14.2.12",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/forms": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.12.tgz",
+ "integrity": "sha512-7abYlGIT2JnAtutQUlH3fQS6QEpbfftgvsVcZJCyvX0rXL3u2w2vUQkDHJH4YJJp3AHFVCH4/l7R4VcaPnrwvA==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "14.2.12",
+ "@angular/core": "14.2.12",
+ "@angular/platform-browser": "14.2.12",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/material": {
+ "version": "14.2.7",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz",
+ "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/animations": "^14.0.0 || ^15.0.0",
+ "@angular/cdk": "14.2.7",
+ "@angular/common": "^14.0.0 || ^15.0.0",
+ "@angular/core": "^14.0.0 || ^15.0.0",
+ "@angular/forms": "^14.0.0 || ^15.0.0",
+ "@angular/platform-browser": "^14.0.0 || ^15.0.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/platform-browser": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.12.tgz",
+ "integrity": "sha512-vOarWym8ucl1gjYWCzdwyBha+MTvL381mvTTUu8aUx6nVhHFjv4bvpjlZnZgojecqUPyxOwmPLLHvCZPJVHZYg==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/animations": "14.2.12",
+ "@angular/common": "14.2.12",
+ "@angular/core": "14.2.12"
+ },
+ "peerDependenciesMeta": {
+ "@angular/animations": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/platform-browser-dynamic": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.12.tgz",
+ "integrity": "sha512-oZhNJeaBmgw8+KBSYpKz2RYqEDyETC+HJXH8dwIFcP6BqqwL2NE70FdSR7EnOa5c41MEtTmMCGhrJSFR60x5/w==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "14.2.12",
+ "@angular/compiler": "14.2.12",
+ "@angular/core": "14.2.12",
+ "@angular/platform-browser": "14.2.12"
+ }
+ },
+ "node_modules/@angular/router": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.12.tgz",
+ "integrity": "sha512-r5tVus5RJDNc4U2v0jMtjPiAS1xDsVsJ70lS313DgZmBDHIVZP1cWIehdxwgNlGwQQtAA36eG7toBwqUU3gb/A==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "14.2.12",
+ "@angular/core": "14.2.12",
+ "@angular/platform-browser": "14.2.12",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@assemblyscript/loader": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
+ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==",
+ "dev": true
+ },
+ "node_modules/@auth0/auth0-angular": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-angular/-/auth0-angular-1.11.1.tgz",
+ "integrity": "sha512-q02tvjbela8TpJEaHbaw1vNkORLTOcjr/mRnxzE1rvIUbSfUGKueR8BTcT+a9mX0KIfF3lArGSF0p01bDO/P3w==",
+ "dependencies": {
+ "@auth0/auth0-spa-js": "^1.22.0",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=12 <=15",
+ "@angular/core": ">=12 <=15",
+ "@angular/router": ">=12 <=15"
+ }
+ },
+ "node_modules/@auth0/auth0-spa-js": {
+ "version": "1.22.5",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.5.tgz",
+ "integrity": "sha512-6gaQcd+Eb8ZBcdQkrrm9undM7dY/rPvVdQN8s7rxxrviUCs7OopEygsfSkHf67IP4HtlCiE8dSW5/AipRUOw/A==",
+ "dependencies": {
+ "abortcontroller-polyfill": "^1.7.3",
+ "browser-tabs-lock": "^1.2.15",
+ "core-js": "^3.25.1",
+ "es-cookie": "~1.3.2",
+ "fast-text-encoding": "^1.0.6",
+ "promise-polyfill": "^8.2.3",
+ "unfetch": "^4.2.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+ "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.20.10",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz",
+ "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz",
+ "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.18.10",
+ "@babel/helper-compilation-targets": "^7.18.9",
+ "@babel/helper-module-transforms": "^7.18.9",
+ "@babel/helpers": "^7.18.9",
+ "@babel/parser": "^7.18.10",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.18.10",
+ "@babel/types": "^7.18.10",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.18.12",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz",
+ "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.10",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+ "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz",
+ "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-explode-assignable-expression": "^7.18.6",
+ "@babel/types": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
+ "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.20.5",
+ "@babel/helper-validator-option": "^7.18.6",
+ "browserslist": "^4.21.3",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.7.tgz",
+ "integrity": "sha512-LtoWbDXOaidEf50hmdDqn9g8VEzsorMexoWMQdQODbvmqYmaF23pBP5VNPAGIFHsFQCIeKokDiz3CH5Y2jlY6w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-member-expression-to-functions": "^7.20.7",
+ "@babel/helper-optimise-call-expression": "^7.18.6",
+ "@babel/helper-replace-supers": "^7.20.7",
+ "@babel/helper-split-export-declaration": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz",
+ "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "regexpu-core": "^5.2.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
+ "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.17.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2",
+ "semver": "^6.1.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0-0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-explode-assignable-expression": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz",
+ "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
+ "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.18.10",
+ "@babel/types": "^7.19.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz",
+ "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+ "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz",
+ "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
+ "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
+ "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-wrap-function": "^7.18.9",
+ "@babel/types": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz",
+ "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-member-expression-to-functions": "^7.20.7",
+ "@babel/helper-optimise-call-expression": "^7.18.6",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers/node_modules/@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
+ "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
+ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz",
+ "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz",
+ "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers/node_modules/@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz",
+ "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
+ "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz",
+ "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz",
+ "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/helper-remap-async-to-generator": "^7.18.9",
+ "@babel/plugin-syntax-async-generators": "^7.8.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-class-properties": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
+ "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-class-static-block": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz",
+ "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-dynamic-import": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
+ "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
+ "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-json-strings": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
+ "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-json-strings": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-logical-assignment-operators": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz",
+ "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
+ "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-numeric-separator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
+ "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
+ "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
+ "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-optional-chaining": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz",
+ "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-methods": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+ "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz",
+ "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.20.5",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
+ "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-dynamic-import": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+ "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz",
+ "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.19.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz",
+ "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz",
+ "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/helper-remap-async-to-generator": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
+ "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.20.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.9.tgz",
+ "integrity": "sha512-hwZN0kr16UkIF/kR9F9x8gd1kTkQl1vyAF2lkUmlTuCtTKOGLE5blQctuxEeKXwz0dkArQ9RYL8+HLb/75KGMA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz",
+ "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-compilation-targets": "^7.20.7",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-optimise-call-expression": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-replace-supers": "^7.20.7",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz",
+ "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/template": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz",
+ "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
+ "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz",
+ "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
+ "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.18.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz",
+ "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz",
+ "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.18.9",
+ "@babel/helper-function-name": "^7.18.9",
+ "@babel/helper-plugin-utils": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz",
+ "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
+ "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.7.tgz",
+ "integrity": "sha512-+1IVLD+dHOzRZWNFFSoyPZz4ffsVmOP+OhhjeahLKpU97v/52LcCb9RabRl5eHM1/HAuH5Dl0q9Pyzrq1v2otQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.7.tgz",
+ "integrity": "sha512-76jqqFiFdCD+RJwEdtBHUG2/rEKQAmpejPbAKyQECEE3/y4U5CMPc9IXvipS990vgQhzq+ZRw6WJ+q4xJ/P24w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-simple-access": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.19.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz",
+ "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-module-transforms": "^7.19.6",
+ "@babel/helper-plugin-utils": "^7.19.0",
+ "@babel/helper-validator-identifier": "^7.19.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
+ "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz",
+ "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.20.5",
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz",
+ "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
+ "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/helper-replace-supers": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz",
+ "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
+ "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz",
+ "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "regenerator-transform": "^0.15.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
+ "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-runtime": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz",
+ "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "babel-plugin-polyfill-corejs2": "^0.3.2",
+ "babel-plugin-polyfill-corejs3": "^0.5.3",
+ "babel-plugin-polyfill-regenerator": "^0.4.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
+ "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz",
+ "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
+ "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz",
+ "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz",
+ "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz",
+ "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
+ "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz",
+ "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.18.8",
+ "@babel/helper-compilation-targets": "^7.18.9",
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/helper-validator-option": "^7.18.6",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9",
+ "@babel/plugin-proposal-async-generator-functions": "^7.18.10",
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@babel/plugin-proposal-class-static-block": "^7.18.6",
+ "@babel/plugin-proposal-dynamic-import": "^7.18.6",
+ "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+ "@babel/plugin-proposal-json-strings": "^7.18.6",
+ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+ "@babel/plugin-proposal-numeric-separator": "^7.18.6",
+ "@babel/plugin-proposal-object-rest-spread": "^7.18.9",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
+ "@babel/plugin-proposal-optional-chaining": "^7.18.9",
+ "@babel/plugin-proposal-private-methods": "^7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+ "@babel/plugin-syntax-import-assertions": "^7.18.6",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5",
+ "@babel/plugin-transform-arrow-functions": "^7.18.6",
+ "@babel/plugin-transform-async-to-generator": "^7.18.6",
+ "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
+ "@babel/plugin-transform-block-scoping": "^7.18.9",
+ "@babel/plugin-transform-classes": "^7.18.9",
+ "@babel/plugin-transform-computed-properties": "^7.18.9",
+ "@babel/plugin-transform-destructuring": "^7.18.9",
+ "@babel/plugin-transform-dotall-regex": "^7.18.6",
+ "@babel/plugin-transform-duplicate-keys": "^7.18.9",
+ "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
+ "@babel/plugin-transform-for-of": "^7.18.8",
+ "@babel/plugin-transform-function-name": "^7.18.9",
+ "@babel/plugin-transform-literals": "^7.18.9",
+ "@babel/plugin-transform-member-expression-literals": "^7.18.6",
+ "@babel/plugin-transform-modules-amd": "^7.18.6",
+ "@babel/plugin-transform-modules-commonjs": "^7.18.6",
+ "@babel/plugin-transform-modules-systemjs": "^7.18.9",
+ "@babel/plugin-transform-modules-umd": "^7.18.6",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6",
+ "@babel/plugin-transform-new-target": "^7.18.6",
+ "@babel/plugin-transform-object-super": "^7.18.6",
+ "@babel/plugin-transform-parameters": "^7.18.8",
+ "@babel/plugin-transform-property-literals": "^7.18.6",
+ "@babel/plugin-transform-regenerator": "^7.18.6",
+ "@babel/plugin-transform-reserved-words": "^7.18.6",
+ "@babel/plugin-transform-shorthand-properties": "^7.18.6",
+ "@babel/plugin-transform-spread": "^7.18.9",
+ "@babel/plugin-transform-sticky-regex": "^7.18.6",
+ "@babel/plugin-transform-template-literals": "^7.18.9",
+ "@babel/plugin-transform-typeof-symbol": "^7.18.9",
+ "@babel/plugin-transform-unicode-escapes": "^7.18.10",
+ "@babel/plugin-transform-unicode-regex": "^7.18.6",
+ "@babel/preset-modules": "^0.1.5",
+ "@babel/types": "^7.18.10",
+ "babel-plugin-polyfill-corejs2": "^0.3.2",
+ "babel-plugin-polyfill-corejs3": "^0.5.3",
+ "babel-plugin-polyfill-regenerator": "^0.4.0",
+ "core-js-compat": "^3.22.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
+ "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+ "@babel/plugin-transform-dotall-regex": "^7.4.4",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
+ "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
+ "dev": true,
+ "dependencies": {
+ "regenerator-runtime": "^0.13.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
+ "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.18.10",
+ "@babel/types": "^7.18.10"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.20.10",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.10.tgz",
+ "integrity": "sha512-oSf1juCgymrSez8NI4A2sr4+uB/mFd9MXplYGPEBnfAuWmmyeVcHa6xLPiaRBcXkcb/28bgxmQLTVwFKE1yfsg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.7",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/@babel/generator": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz",
+ "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
+ "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@csstools/postcss-cascade-layers": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz",
+ "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/selector-specificity": "^2.0.2",
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-color-function": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz",
+ "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-font-format-keywords": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz",
+ "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-hwb-function": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz",
+ "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-ic-unit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz",
+ "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-is-pseudo-class": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz",
+ "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/selector-specificity": "^2.0.0",
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-nested-calc": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz",
+ "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-normalize-display-values": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz",
+ "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-oklab-function": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz",
+ "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-progressive-custom-properties": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz",
+ "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ }
+ },
+ "node_modules/@csstools/postcss-stepped-value-functions": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz",
+ "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-text-decoration-shorthand": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz",
+ "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-trigonometric-functions": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz",
+ "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/postcss-unset-value": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz",
+ "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==",
+ "dev": true,
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/@csstools/selector-specificity": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz",
+ "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==",
+ "dev": true,
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2",
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz",
+ "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz",
+ "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.4.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.19.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
+ "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@gar/promisify": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
+ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
+ "dev": true
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+ "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+ "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@leichtgewicht/ip-codec": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
+ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
+ "dev": true
+ },
+ "node_modules/@ngrx/effects": {
+ "version": "14.3.2",
+ "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-14.3.2.tgz",
+ "integrity": "sha512-6bpGfA44jzwhBcmNaTwVgnFmYOX9iKPFpXyetDe41tVESo1CsNhUBPTmISDXKN9Mx2mwGbsMxrn6QFRypSsKAQ==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^14.0.0",
+ "@ngrx/store": "14.3.2",
+ "rxjs": "^6.5.3 || ^7.5.0"
+ }
+ },
+ "node_modules/@ngrx/store": {
+ "version": "14.3.2",
+ "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-14.3.2.tgz",
+ "integrity": "sha512-XGHjr0arh6gClo8Ce+xqJLvW9PkeXPW2tCo9Z5qMtHFI/z5dUppLVKGmMgD/fQBDyoqWTK5xu+89tDkdeZfRjQ==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^14.0.0",
+ "rxjs": "^6.5.3 || ^7.5.0"
+ }
+ },
+ "node_modules/@ngtools/webpack": {
+ "version": "14.2.10",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.10.tgz",
+ "integrity": "sha512-sLHapZLVub6mEz5b19tf1VfIV1w3tYfg7FNPLeni79aldxu1FbP1v2WmiFAnMzrswqyK0bhTtxrl+Z/CLKqyoQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler-cli": "^14.0.0",
+ "typescript": ">=4.6.2 <4.9",
+ "webpack": "^5.54.0"
+ }
+ },
+ "node_modules/@ngxs/devtools-plugin": {
+ "version": "3.7.6",
+ "resolved": "https://registry.npmjs.org/@ngxs/devtools-plugin/-/devtools-plugin-3.7.6.tgz",
+ "integrity": "sha512-KbFak7ZF8U23z1TDwpkKqReNMco4c958SAJ02JOc89U+O8scpwOCTUh/aJxoy8ZsqV4/F1eb4DeZ5EtXXXznwg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ngxs"
+ },
+ "peerDependencies": {
+ "@angular/core": ">=6.1.0 <16.0.0",
+ "@ngxs/store": "^3.7.6 || ^3.7.6-dev",
+ "rxjs": ">=6.5.5"
+ }
+ },
+ "node_modules/@ngxs/devtools-plugin/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@ngxs/store": {
+ "version": "3.7.6",
+ "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.6.tgz",
+ "integrity": "sha512-ljnrkVrO/HmDakQImWr9G2NbpbNd8MjoPI3Ad2GsIbK/Ey4psEyMGgg6jcaMyhDfDAs2Z69Q+5TVJS0GV4euMw==",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ngxs"
+ },
+ "peerDependencies": {
+ "@angular/core": ">=6.1.0 <16.0.0",
+ "rxjs": ">=6.5.5"
+ }
+ },
+ "node_modules/@ngxs/store/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@npmcli/fs": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
+ "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
+ "dev": true,
+ "dependencies": {
+ "@gar/promisify": "^1.1.3",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@npmcli/git": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz",
+ "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==",
+ "dev": true,
+ "dependencies": {
+ "@npmcli/promise-spawn": "^3.0.0",
+ "lru-cache": "^7.4.4",
+ "mkdirp": "^1.0.4",
+ "npm-pick-manifest": "^7.0.0",
+ "proc-log": "^2.0.0",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^2.0.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@npmcli/git/node_modules/lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@npmcli/installed-package-contents": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz",
+ "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==",
+ "dev": true,
+ "dependencies": {
+ "npm-bundled": "^1.1.1",
+ "npm-normalize-package-bin": "^1.0.1"
+ },
+ "bin": {
+ "installed-package-contents": "index.js"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@npmcli/move-file": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
+ "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
+ "deprecated": "This functionality has been moved to @npmcli/fs",
+ "dev": true,
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@npmcli/node-gyp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz",
+ "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@npmcli/promise-spawn": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz",
+ "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==",
+ "dev": true,
+ "dependencies": {
+ "infer-owner": "^1.0.4"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@npmcli/run-script": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-3.0.3.tgz",
+ "integrity": "sha512-ZXL6qgC5NjwfZJ2nET+ZSLEz/PJgJ/5CU90C2S66dZY4Jw73DasS4ZCXuy/KHWYP0imjJ4VtA+Gebb5BxxKp9Q==",
+ "dev": true,
+ "dependencies": {
+ "@npmcli/node-gyp": "^2.0.0",
+ "@npmcli/promise-spawn": "^3.0.0",
+ "node-gyp": "^8.4.1",
+ "read-package-json-fast": "^2.0.3"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "node_modules/@schematics/angular": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.0.7.tgz",
+ "integrity": "sha512-I0v1gNFpm9ReL/hUzwjjOa+hk0qvlXv/vjITAWnlUV5dba6FZxzwsrTGsGO6t5XMNsm6QtwpDYDRdy9uy/n/1g==",
+ "dev": true,
+ "dependencies": {
+ "@angular-devkit/core": "14.0.7",
+ "@angular-devkit/schematics": "14.0.7",
+ "jsonc-parser": "3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@schematics/angular/node_modules/@angular-devkit/core": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz",
+ "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.0.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.5.2"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@schematics/angular/node_modules/jsonc-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
+ "dev": true
+ },
+ "node_modules/@schematics/angular/node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@schematics/angular/node_modules/source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@schematics/angular/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
+ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
+ "dev": true
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/bonjour": {
+ "version": "3.5.10",
+ "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz",
+ "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/chrome": {
+ "version": "0.0.204",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.204.tgz",
+ "integrity": "sha512-EvnHfxMHUWP5EAlRMK66uIEUiy36t72vg5RwmzQv9tdIl2ZmAp92NwvmEZJKpbRnIMTEc2BmSmtrFiEISUJ0Sw==",
+ "dev": true,
+ "dependencies": {
+ "@types/filesystem": "*",
+ "@types/har-format": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect-history-api-fallback": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz",
+ "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==",
+ "dev": true,
+ "dependencies": {
+ "@types/express-serve-static-core": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
+ "dev": true
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.13",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
+ "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/dateformat": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.0.tgz",
+ "integrity": "sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==",
+ "dev": true
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.4.10",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
+ "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
+ "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "0.0.51",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz",
+ "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.31",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.31",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
+ "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "node_modules/@types/filesystem": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz",
+ "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/filewriter": "*"
+ }
+ },
+ "node_modules/@types/filewriter": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz",
+ "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
+ "dev": true
+ },
+ "node_modules/@types/har-format": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
+ "integrity": "sha512-o0J30wqycjF5miWDKYKKzzOU1ZTLuA42HZ4HE7/zqTOc/jTLdQ5NhYWvsRQo45Nfi1KHoRdNhteSI4BAxTF1Pg==",
+ "dev": true
+ },
+ "node_modules/@types/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
+ },
+ "node_modules/@types/http-proxy": {
+ "version": "1.17.9",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
+ "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/jasmine": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.1.tgz",
+ "integrity": "sha512-Vu8l+UGcshYmV1VWwULgnV/2RDbBaO6i2Ptx7nd//oJPIZGhoI1YLST4VKagD2Pq/Bc2/7zvtvhM7F3p4SN7kQ==",
+ "dev": true
+ },
+ "node_modules/@types/jquery": {
+ "version": "3.5.14",
+ "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
+ "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
+ "dev": true,
+ "dependencies": {
+ "@types/sizzle": "*"
+ }
+ },
+ "node_modules/@types/jsbn": {
+ "version": "1.2.30",
+ "resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.30.tgz",
+ "integrity": "sha512-VZouplBofjq3YOIHLNRBDxILs/nAArdTZ2QP1ooflyhS+yPExWsFE+i2paBIBb7OI3NJShfcde/nogqk4SPB/Q==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "8.5.9",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz",
+ "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
+ },
+ "node_modules/@types/mime": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
+ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
+ },
+ "node_modules/@types/node": {
+ "version": "18.11.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz",
+ "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng=="
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+ "dev": true
+ },
+ "node_modules/@types/q": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
+ "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==",
+ "dev": true
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+ },
+ "node_modules/@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "dev": true
+ },
+ "node_modules/@types/selenium-webdriver": {
+ "version": "3.0.20",
+ "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.20.tgz",
+ "integrity": "sha512-6d8Q5fqS9DWOXEhMDiF6/2FjyHdmP/jSTAUyeQR7QwrFeNmYyzmvGxD5aLIHL445HjWgibs0eAig+KPnbaesXA==",
+ "dev": true
+ },
+ "node_modules/@types/semver": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+ "dev": true
+ },
+ "node_modules/@types/serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==",
+ "dev": true,
+ "dependencies": {
+ "@types/express": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "dependencies": {
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/sizzle": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
+ "dev": true
+ },
+ "node_modules/@types/sockjs": {
+ "version": "0.3.33",
+ "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
+ "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/three": {
+ "version": "0.143.2",
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.143.2.tgz",
+ "integrity": "sha512-HjsRWvd6rsXViFeOdU97/pHNDQknzJbFI0/5MrQ0joOlK0uixQH40sDJs/LwkNHhFYUvVENXAUQdKDeiQChHDw==",
+ "dev": true,
+ "dependencies": {
+ "@types/webxr": "*"
+ }
+ },
+ "node_modules/@types/w3c-web-usb": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz",
+ "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==",
+ "dev": true
+ },
+ "node_modules/@types/webxr": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.0.tgz",
+ "integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==",
+ "dev": true
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+ "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz",
+ "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.47.0",
+ "@typescript-eslint/type-utils": "5.47.0",
+ "@typescript-eslint/utils": "5.47.0",
+ "debug": "^4.3.4",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz",
+ "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.47.0",
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/typescript-estree": "5.47.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz",
+ "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/visitor-keys": "5.47.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz",
+ "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "5.47.0",
+ "@typescript-eslint/utils": "5.47.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
+ "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz",
+ "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/visitor-keys": "5.47.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz",
+ "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.47.0",
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/typescript-estree": "5.47.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
+ "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.47.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+ "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+ "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+ "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+ "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+ "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+ "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+ "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+ "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+ "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+ "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+ "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/helper-wasm-section": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-opt": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "@webassemblyjs/wast-printer": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+ "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+ "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+ "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+ "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webpack-cli/configtest": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
+ "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
+ "peerDependencies": {
+ "webpack": "4.x.x || 5.x.x",
+ "webpack-cli": "4.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/info": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
+ "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
+ "dependencies": {
+ "envinfo": "^7.7.3"
+ },
+ "peerDependencies": {
+ "webpack-cli": "4.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/serve": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
+ "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
+ "peerDependencies": {
+ "webpack-cli": "4.x.x"
+ },
+ "peerDependenciesMeta": {
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
+ },
+ "node_modules/@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "dev": true
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "node_modules/abortcontroller-polyfill": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
+ "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ=="
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-import-assertions": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+ "peerDependencies": {
+ "acorn": "^8"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/adjust-sourcemap-loader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
+ "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "regex-parser": "^2.2.11"
+ },
+ "engines": {
+ "node": ">=8.9"
+ }
+ },
+ "node_modules/adm-zip": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
+ "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.0",
+ "depd": "^1.1.2",
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/agentkeepalive/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/angular2-template-loader": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz",
+ "integrity": "sha512-jBSrm2yDsTA48GG0H57upn8rmTfJS3/R7EhUeAL/3ryWS8deT9You8UQKWpW4eVSEY7uMJ52iFwpOYXc8QEtGg==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^0.2.15"
+ }
+ },
+ "node_modules/angular2-template-loader/node_modules/big.js": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
+ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/angular2-template-loader/node_modules/emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/angular2-template-loader/node_modules/json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/angular2-template-loader/node_modules/loader-utils": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+ "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^3.1.3",
+ "emojis-list": "^2.0.0",
+ "json5": "^0.5.0",
+ "object-assign": "^4.0.1"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-html-community": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+ "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+ "dev": true,
+ "engines": [
+ "node >= 0.8.0"
+ ],
+ "bin": {
+ "ansi-html": "bin/ansi-html"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/app-module-path": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz",
+ "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==",
+ "dev": true
+ },
+ "node_modules/aproba": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+ "dev": true
+ },
+ "node_modules/are-we-there-yet": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+ "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+ "dev": true,
+ "dependencies": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+ "dev": true
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/ast-module-types": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz",
+ "integrity": "sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/auth0": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/auth0/-/auth0-3.0.1.tgz",
+ "integrity": "sha512-3VZd+irYBIo16rEtYUnzK8FoOCgShiYsv1foQA5Zzcsj44RLq5mSVhyuYTIg9/h0Al2CXWalxYK/lrMtkQftzw==",
+ "dependencies": {
+ "axios": "^0.27.2",
+ "form-data": "^3.0.1",
+ "jsonwebtoken": "^9.0.0",
+ "jwks-rsa": "^3.0.0",
+ "lru-memoizer": "^2.1.4",
+ "rest-facade": "^1.16.3",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.13",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
+ "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "caniuse-lite": "^1.0.30001426",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true
+ },
+ "node_modules/axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "dependencies": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/babel-loader": {
+ "version": "8.2.5",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz",
+ "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==",
+ "dev": true,
+ "dependencies": {
+ "find-cache-dir": "^3.3.1",
+ "loader-utils": "^2.0.0",
+ "make-dir": "^3.1.0",
+ "schema-utils": "^2.6.5"
+ },
+ "engines": {
+ "node": ">= 8.9"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0",
+ "webpack": ">=2"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
+ "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.17.7",
+ "@babel/helper-define-polyfill-provider": "^0.3.3",
+ "semver": "^6.1.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz",
+ "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.3.2",
+ "core-js-compat": "^3.21.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz",
+ "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.3.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base64-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true,
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
+ },
+ "node_modules/batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+ "dev": true
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/blocking-proxy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
+ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "blocking-proxy": "built/lib/bin.js"
+ },
+ "engines": {
+ "node": ">=6.9.x"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/bonjour-service": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz",
+ "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==",
+ "dev": true,
+ "dependencies": {
+ "array-flatten": "^2.1.2",
+ "dns-equal": "^1.0.0",
+ "fast-deep-equal": "^3.1.3",
+ "multicast-dns": "^7.2.5"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-tabs-lock": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.2.15.tgz",
+ "integrity": "sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "lodash": ">=4.17.21"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
+ "node-releases": "^2.0.6",
+ "update-browserslist-db": "^1.0.9"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/browserstack": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz",
+ "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==",
+ "dev": true,
+ "dependencies": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "node_modules/browserstack/node_modules/agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "dependencies": {
+ "es6-promisify": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/browserstack/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/browserstack/node_modules/https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "node_modules/builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/builtins": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
+ "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.0.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cacache": {
+ "version": "16.1.2",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz",
+ "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==",
+ "dev": true,
+ "dependencies": {
+ "@npmcli/fs": "^2.1.0",
+ "@npmcli/move-file": "^2.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "glob": "^8.0.1",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "mkdirp": "^1.0.4",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^1.1.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/cacache/node_modules/lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "dependencies": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001441",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz",
+ "integrity": "sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ }
+ ]
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/change-case": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.3.1.tgz",
+ "integrity": "sha512-3HE5jrTqqn9jeKzD0+yWi7FU4OMicLbwB57ph4bpwEn5jGi3hZug5WjZjnBD2RY7YyTKAAck86ACfShXUWJKLg==",
+ "dependencies": {
+ "camel-case": "^1.1.1",
+ "constant-case": "^1.1.0",
+ "dot-case": "^1.1.0",
+ "is-lower-case": "^1.1.0",
+ "is-upper-case": "^1.1.0",
+ "lower-case": "^1.1.1",
+ "lower-case-first": "^1.0.0",
+ "param-case": "^1.1.0",
+ "pascal-case": "^1.1.0",
+ "path-case": "^1.1.0",
+ "sentence-case": "^1.1.1",
+ "snake-case": "^1.1.0",
+ "swap-case": "^1.1.0",
+ "title-case": "^1.1.0",
+ "upper-case": "^1.1.1",
+ "upper-case-first": "^1.1.0"
+ }
+ },
+ "node_modules/change-case/node_modules/camel-case": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz",
+ "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==",
+ "dependencies": {
+ "sentence-case": "^1.1.1",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "node_modules/change-case/node_modules/dot-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz",
+ "integrity": "sha512-NzEIt12UjECXi6JZ/R/nBey6EE1qCN0yUTEFaPIaKW0AcOEwlKqujtcJVbtSfLNnj3CDoXLQyli79vAaqohyvw==",
+ "dependencies": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "node_modules/change-case/node_modules/lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ },
+ "node_modules/change-case/node_modules/param-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz",
+ "integrity": "sha512-gksk6zeZQxwBm1AHsKh+XDFsTGf1LvdZSkkpSIkfDtzW+EQj/P2PBgNb3Cs0Y9Xxqmbciv2JZe3fWU6Xbher+Q==",
+ "dependencies": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "node_modules/change-case/node_modules/pascal-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz",
+ "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==",
+ "dependencies": {
+ "camel-case": "^1.1.1",
+ "upper-case-first": "^1.1.0"
+ }
+ },
+ "node_modules/chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/clean-css": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
+ "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==",
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
+ }
+ },
+ "node_modules/clean-css/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true,
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
+ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/compression/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/connect-history-api-fallback": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
+ "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/connect/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/connect/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+ "dev": true
+ },
+ "node_modules/constant-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz",
+ "integrity": "sha512-FQ/HuOuSnX6nIF8OnofRWj+KnOpGAHXQpOKHmsL1sAnuLwu6r5mHGK+mJc0SkHkbmNfcU/SauqXLTEOL1JQfJA==",
+ "dependencies": {
+ "snake-case": "^1.1.0",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
+ },
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+ "dev": true
+ },
+ "node_modules/cookiejar": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
+ "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ=="
+ },
+ "node_modules/copy-anything": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+ "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+ "dev": true,
+ "dependencies": {
+ "is-what": "^3.14.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/copy-webpack-plugin": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz",
+ "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==",
+ "dev": true,
+ "dependencies": {
+ "fast-glob": "^3.2.11",
+ "glob-parent": "^6.0.1",
+ "globby": "^13.1.1",
+ "normalize-path": "^3.0.0",
+ "schema-utils": "^4.0.0",
+ "serialize-javascript": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/globby": {
+ "version": "13.1.3",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz",
+ "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==",
+ "dev": true,
+ "dependencies": {
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.11",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/slash": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
+ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz",
+ "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==",
+ "hasInstallScript": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz",
+ "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.21.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dev": true,
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/critters": {
+ "version": "0.0.16",
+ "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz",
+ "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "css-select": "^4.2.0",
+ "parse5": "^6.0.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.1",
+ "postcss": "^8.3.7",
+ "pretty-bytes": "^5.3.0"
+ }
+ },
+ "node_modules/critters/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/critters/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/critters/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/critters/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/critters/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/critters/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "node_modules/critters/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-blank-pseudo": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz",
+ "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "bin": {
+ "css-blank-pseudo": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-has-pseudo": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz",
+ "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "bin": {
+ "css-has-pseudo": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-line-break": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+ "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+ "dependencies": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "node_modules/css-loader": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
+ "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.7",
+ "postcss-modules-extract-imports": "^3.0.0",
+ "postcss-modules-local-by-default": "^4.0.0",
+ "postcss-modules-scope": "^3.0.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/css-prefers-color-scheme": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz",
+ "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==",
+ "dev": true,
+ "bin": {
+ "css-prefers-color-scheme": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssdb": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz",
+ "integrity": "sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
+ "dev": true
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/date-format": {
+ "version": "4.0.14",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
+ "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/dateformat": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz",
+ "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/deepmerge": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
+ "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/default-gateway": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
+ "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
+ "dev": true,
+ "dependencies": {
+ "execa": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==",
+ "dev": true,
+ "dependencies": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/del/node_modules/array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dev": true,
+ "dependencies": {
+ "array-uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/del/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/del/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/del/node_modules/globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/del/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/del/node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "dev": true
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dependency-graph": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+ "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/dependency-tree": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-8.1.2.tgz",
+ "integrity": "sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.20.3",
+ "debug": "^4.3.1",
+ "filing-cabinet": "^3.0.1",
+ "precinct": "^8.0.0",
+ "typescript": "^3.9.7"
+ },
+ "bin": {
+ "dependency-tree": "bin/cli.js"
+ },
+ "engines": {
+ "node": "^10.13 || ^12 || >=14"
+ }
+ },
+ "node_modules/dependency-tree/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/dependency-tree/node_modules/typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "dev": true
+ },
+ "node_modules/detective-amd": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-3.1.2.tgz",
+ "integrity": "sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^3.0.0",
+ "escodegen": "^2.0.0",
+ "get-amd-module-type": "^3.0.0",
+ "node-source-walk": "^4.2.0"
+ },
+ "bin": {
+ "detective-amd": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/detective-cjs": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-3.1.3.tgz",
+ "integrity": "sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^3.0.0",
+ "node-source-walk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/detective-es6": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz",
+ "integrity": "sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw==",
+ "dev": true,
+ "dependencies": {
+ "node-source-walk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/detective-less": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/detective-less/-/detective-less-1.0.2.tgz",
+ "integrity": "sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.0.0",
+ "gonzales-pe": "^4.2.3",
+ "node-source-walk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 6.0"
+ }
+ },
+ "node_modules/detective-postcss": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-5.1.3.tgz",
+ "integrity": "sha512-Wo7PUpF6wqeT1aRgajdyIdDRjFFJVxlXPRAlT1aankH/RVOgrJuEZFZ4ABxYXdzaRPO5Lkg8rHxsxpLnxdJIYA==",
+ "dev": true,
+ "dependencies": {
+ "is-url": "^1.2.4",
+ "postcss": "^8.4.6",
+ "postcss-values-parser": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/detective-sass": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-3.0.2.tgz",
+ "integrity": "sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g==",
+ "dev": true,
+ "dependencies": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/detective-scss": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-2.0.2.tgz",
+ "integrity": "sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg==",
+ "dev": true,
+ "dependencies": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/detective-stylus": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-1.0.3.tgz",
+ "integrity": "sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q==",
+ "dev": true
+ },
+ "node_modules/detective-typescript": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-7.0.2.tgz",
+ "integrity": "sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "^4.33.0",
+ "ast-module-types": "^2.7.1",
+ "node-source-walk": "^4.2.0",
+ "typescript": "^3.9.10"
+ },
+ "engines": {
+ "node": "^10.13 || >=12.0.0"
+ }
+ },
+ "node_modules/detective-typescript/node_modules/@typescript-eslint/types": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz",
+ "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==",
+ "dev": true,
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/detective-typescript/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz",
+ "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "4.33.0",
+ "@typescript-eslint/visitor-keys": "4.33.0",
+ "debug": "^4.3.1",
+ "globby": "^11.0.3",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/detective-typescript/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz",
+ "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "4.33.0",
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/detective-typescript/node_modules/ast-module-types": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz",
+ "integrity": "sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw==",
+ "dev": true
+ },
+ "node_modules/detective-typescript/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/detective-typescript/node_modules/typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
+ "dev": true
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dns-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+ "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
+ "dev": true
+ },
+ "node_modules/dns-packet": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz",
+ "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==",
+ "dev": true,
+ "dependencies": {
+ "@leichtgewicht/ip-codec": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-converter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+ "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "dependencies": {
+ "utila": "~0.4"
+ }
+ },
+ "node_modules/dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==",
+ "dev": true,
+ "dependencies": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/ecc-jsbn/node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.284",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
+ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/encoding/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
+ "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
+ "dev": true,
+ "dependencies": {
+ "@types/cookie": "^0.4.1",
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.0.3",
+ "ws": "~8.2.3"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
+ "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
+ "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/envinfo": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "dev": true
+ },
+ "node_modules/errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "prr": "~1.0.1"
+ },
+ "bin": {
+ "errno": "cli.js"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-cookie": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz",
+ "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q=="
+ },
+ "node_modules/es-module-lexer": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
+ },
+ "node_modules/es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+ "dev": true
+ },
+ "node_modules/es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
+ "dev": true,
+ "dependencies": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz",
+ "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/linux-loong64": "0.15.5",
+ "esbuild-android-64": "0.15.5",
+ "esbuild-android-arm64": "0.15.5",
+ "esbuild-darwin-64": "0.15.5",
+ "esbuild-darwin-arm64": "0.15.5",
+ "esbuild-freebsd-64": "0.15.5",
+ "esbuild-freebsd-arm64": "0.15.5",
+ "esbuild-linux-32": "0.15.5",
+ "esbuild-linux-64": "0.15.5",
+ "esbuild-linux-arm": "0.15.5",
+ "esbuild-linux-arm64": "0.15.5",
+ "esbuild-linux-mips64le": "0.15.5",
+ "esbuild-linux-ppc64le": "0.15.5",
+ "esbuild-linux-riscv64": "0.15.5",
+ "esbuild-linux-s390x": "0.15.5",
+ "esbuild-netbsd-64": "0.15.5",
+ "esbuild-openbsd-64": "0.15.5",
+ "esbuild-sunos-64": "0.15.5",
+ "esbuild-windows-32": "0.15.5",
+ "esbuild-windows-64": "0.15.5",
+ "esbuild-windows-arm64": "0.15.5"
+ }
+ },
+ "node_modules/esbuild-android-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz",
+ "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-android-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz",
+ "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-darwin-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz",
+ "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-darwin-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz",
+ "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-freebsd-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz",
+ "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-freebsd-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz",
+ "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-32": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz",
+ "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz",
+ "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-arm": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz",
+ "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz",
+ "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-mips64le": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz",
+ "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-ppc64le": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz",
+ "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-riscv64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz",
+ "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-s390x": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz",
+ "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-netbsd-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz",
+ "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-openbsd-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz",
+ "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-sunos-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz",
+ "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-wasm": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz",
+ "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==",
+ "dev": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-32": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz",
+ "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz",
+ "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz",
+ "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/escodegen/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.30.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz",
+ "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/eslintrc": "^1.4.0",
+ "@humanwhocodes/config-array": "^0.11.8",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.1.1",
+ "eslint-utils": "^3.0.0",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.4.0",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "grapheme-splitter": "^1.0.4",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-sdsl": "^4.1.4",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "regexpp": "^3.2.0",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+ "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+ "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+ "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.19.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
+ "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+ "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.8.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventemitter-asyncresource": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz",
+ "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==",
+ "dev": true
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.1",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.5.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.11.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/express/node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "dev": true
+ },
+ "node_modules/express/node_modules/cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/finalhandler": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/express/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "node_modules/external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "dependencies": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ]
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
+ },
+ "node_modules/fast-text-encoding": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
+ "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
+ "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/filing-cabinet": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.3.0.tgz",
+ "integrity": "sha512-Tnbpbme1ONaHXV5DGcw0OFpcfP3p2itRf5VXO1bguBXdIewDbK6ZFBK//DGKM0BuCzaQLQNY4f5gljzxY1VCUw==",
+ "dev": true,
+ "dependencies": {
+ "app-module-path": "^2.2.0",
+ "commander": "^2.20.3",
+ "debug": "^4.3.3",
+ "enhanced-resolve": "^5.8.3",
+ "is-relative-path": "^1.0.2",
+ "module-definition": "^3.3.1",
+ "module-lookup-amd": "^7.0.1",
+ "resolve": "^1.21.0",
+ "resolve-dependency-path": "^2.0.0",
+ "sass-lookup": "^3.0.0",
+ "stylus-lookup": "^3.0.1",
+ "tsconfig-paths": "^3.10.1",
+ "typescript": "^3.9.7"
+ },
+ "bin": {
+ "filing-cabinet": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/filing-cabinet/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/filing-cabinet/node_modules/typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/finalhandler/node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/find-cache-dir": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+ "dev": true,
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "node_modules/flatten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
+ "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
+ "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
+ "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
+ "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
+ "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://www.patreon.com/infusion"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-monkey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+ "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "node_modules/gauge": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+ "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+ "dev": true,
+ "dependencies": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^3.0.7",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-amd-module-type": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz",
+ "integrity": "sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^3.0.0",
+ "node-source-walk": "^4.2.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-own-enumerable-property-symbols": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
+ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
+ "dev": true
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/gl-matrix": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
+ "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
+ },
+ "node_modules/glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gonzales-pe": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
+ "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "gonzales": "bin/gonzales.js"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ },
+ "node_modules/grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "node_modules/graphviz": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.9.tgz",
+ "integrity": "sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg==",
+ "dev": true,
+ "dependencies": {
+ "temp": "~0.4.0"
+ },
+ "engines": {
+ "node": ">=0.6.8"
+ }
+ },
+ "node_modules/handle-thing": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+ "dev": true
+ },
+ "node_modules/har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "deprecated": "this library is no longer supported",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/har-validator/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/har-validator/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-ansi/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+ "dev": true
+ },
+ "node_modules/hdr-histogram-js": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz",
+ "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==",
+ "dev": true,
+ "dependencies": {
+ "@assemblyscript/loader": "^0.10.1",
+ "base64-js": "^1.2.0",
+ "pako": "^1.0.3"
+ }
+ },
+ "node_modules/hdr-histogram-percentiles-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz",
+ "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==",
+ "dev": true
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz",
+ "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^7.5.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/hosted-git-info/node_modules/lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ }
+ },
+ "node_modules/hpack.js/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/hpack.js/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/hpack.js/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/html-entities": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
+ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
+ "dev": true
+ },
+ "node_modules/html-loader": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.1.2.tgz",
+ "integrity": "sha512-9WQlLiAV5N9fCna4MUmBW/ifaUbuFZ2r7IZmtXzhyfyi4zgPEjXsmsYCKs+yT873MzRj+f1WMjuAiPNA7C6Tcw==",
+ "dependencies": {
+ "html-minifier-terser": "^6.0.2",
+ "parse5": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/html-loader/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ },
+ "node_modules/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+ "dependencies": {
+ "camel-case": "^4.1.2",
+ "clean-css": "^5.2.2",
+ "commander": "^8.3.0",
+ "he": "^1.2.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.10.0"
+ },
+ "bin": {
+ "html-minifier-terser": "cli.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/html-webpack-inline-source-plugin": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-1.0.0-beta.2.tgz",
+ "integrity": "sha512-ydsEKdp0tnbmnqRAH2WSSMXerCNYhjes5b79uvP2BU3p6cyk+6ucNMsw5b5xD1QxphgvBBA3QqVmdcpu8QLlRQ==",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5",
+ "slash": "^1.0.0",
+ "source-map-url": "^0.4.0"
+ }
+ },
+ "node_modules/html-webpack-inline-source-plugin/node_modules/slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/html-webpack-plugin": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+ "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+ "dependencies": {
+ "@types/html-minifier-terser": "^6.0.0",
+ "html-minifier-terser": "^6.0.2",
+ "lodash": "^4.17.21",
+ "pretty-error": "^4.0.0",
+ "tapable": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/html-webpack-plugin"
+ },
+ "peerDependencies": {
+ "webpack": "^5.20.0"
+ }
+ },
+ "node_modules/html2canvas": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+ "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+ "dependencies": {
+ "css-line-break": "^2.1.0",
+ "text-segmentation": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.0.0",
+ "domutils": "^2.5.2",
+ "entities": "^2.0.0"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+ "dev": true
+ },
+ "node_modules/http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
+ "dev": true
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-errors/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+ "dev": true
+ },
+ "node_modules/http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "dependencies": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/http-proxy-middleware": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
+ "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
+ "dev": true,
+ "dependencies": {
+ "@types/http-proxy": "^1.17.8",
+ "http-proxy": "^1.18.1",
+ "is-glob": "^4.0.1",
+ "is-plain-obj": "^3.0.0",
+ "micromatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "@types/express": "^4.17.13"
+ },
+ "peerDependenciesMeta": {
+ "@types/express": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/ignore-walk": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz",
+ "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==",
+ "dev": true,
+ "dependencies": {
+ "minimatch": "^5.0.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true
+ },
+ "node_modules/immutable": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz",
+ "integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==",
+ "dev": true
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==",
+ "dev": true
+ },
+ "node_modules/infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ini": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz",
+ "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==",
+ "dev": true,
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/inquirer": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz",
+ "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.1",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.21",
+ "mute-stream": "0.0.8",
+ "ora": "^5.4.1",
+ "run-async": "^2.4.0",
+ "rxjs": "^7.5.5",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/inquirer/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/inquirer/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/inquirer/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inquirer/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/interpret": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/ip": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
+ "dev": true
+ },
+ "node_modules/ipaddr.js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
+ "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-lambda": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+ "dev": true
+ },
+ "node_modules/is-lower-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz",
+ "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==",
+ "dependencies": {
+ "lower-case": "^1.1.0"
+ }
+ },
+ "node_modules/is-lower-case/node_modules/lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "dependencies": {
+ "is-path-inside": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-in-cwd/node_modules/is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==",
+ "dev": true,
+ "dependencies": {
+ "path-is-inside": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-relative-path": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.2.tgz",
+ "integrity": "sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA==",
+ "dev": true
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-upper-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz",
+ "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==",
+ "dependencies": {
+ "upper-case": "^1.1.0"
+ }
+ },
+ "node_modules/is-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "dev": true
+ },
+ "node_modules/is-url-superb": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz",
+ "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-what": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+ "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
+ "dev": true
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "node_modules/isbinaryfile": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+ "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/gjtorikian/"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/jasmine": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.3.0.tgz",
+ "integrity": "sha512-ieBmwkd8L1DXnvSnxx7tecXgA0JDgMXPAwBcqM4lLPedJeI9hTHuWifPynTC+dLe4Y+GkSPSlbqqrmYIgGzYUw==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.6",
+ "jasmine-core": "^4.3.0"
+ },
+ "bin": {
+ "jasmine": "bin/jasmine.js"
+ }
+ },
+ "node_modules/jasmine-core": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.1.tgz",
+ "integrity": "sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==",
+ "dev": true
+ },
+ "node_modules/jasmine/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/jasmine/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/jasmine/node_modules/jasmine-core": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.5.0.tgz",
+ "integrity": "sha512-9PMzyvhtocxb3aXJVOPqBDswdgyAeSB81QnLop4npOpbqnheaTEwPc9ZloQeVswugPManznQBjD8kWDTjlnHuw==",
+ "dev": true
+ },
+ "node_modules/jasmine/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/jasminewd2": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
+ "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6.9.x"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/jose": {
+ "version": "4.11.1",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz",
+ "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/js-sdsl": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
+ "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsbn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
+ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
+ },
+ "node_modules/jsbn-rsa": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/jsbn-rsa/-/jsbn-rsa-1.0.4.tgz",
+ "integrity": "sha512-unHyEPFGjr6WCzrcMiwdNhYMlq4gXt6Hg5JuKOyE7OXJ7GbVMpottnqsUkPeZCAYqByAkn4N8gJwCpnacduOew=="
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
+ "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz",
+ "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==",
+ "dev": true
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ]
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+ "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash": "^4.17.21",
+ "ms": "^2.1.1",
+ "semver": "^7.3.8"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dev": true,
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/jszip/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/jszip/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jwks-rsa": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.0.0.tgz",
+ "integrity": "sha512-x9qNrP/kD6tOfrLzBVC5HaneBTR+fCEGIjwk/xSdl+KA7Tzf+R3oiY9ibrONKVLF9fR0V03enkitYPZkO65fAQ==",
+ "dependencies": {
+ "@types/express": "^4.17.14",
+ "@types/jsonwebtoken": "^8.5.9",
+ "debug": "^4.3.4",
+ "jose": "^4.10.3",
+ "limiter": "^1.1.5",
+ "lru-memoizer": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/karma": {
+ "version": "6.3.20",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz",
+ "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==",
+ "dev": true,
+ "dependencies": {
+ "@colors/colors": "1.5.0",
+ "body-parser": "^1.19.0",
+ "braces": "^3.0.2",
+ "chokidar": "^3.5.1",
+ "connect": "^3.7.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.1",
+ "glob": "^7.1.7",
+ "graceful-fs": "^4.2.6",
+ "http-proxy": "^1.18.1",
+ "isbinaryfile": "^4.0.8",
+ "lodash": "^4.17.21",
+ "log4js": "^6.4.1",
+ "mime": "^2.5.2",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.5",
+ "qjobs": "^1.2.0",
+ "range-parser": "^1.2.1",
+ "rimraf": "^3.0.2",
+ "socket.io": "^4.4.1",
+ "source-map": "^0.6.1",
+ "tmp": "^0.2.1",
+ "ua-parser-js": "^0.7.30",
+ "yargs": "^16.1.1"
+ },
+ "bin": {
+ "karma": "bin/karma"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/karma-chrome-launcher": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz",
+ "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==",
+ "dev": true,
+ "dependencies": {
+ "which": "^1.2.1"
+ }
+ },
+ "node_modules/karma-chrome-launcher/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/karma-jasmine": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.0.1.tgz",
+ "integrity": "sha512-FkL1Kk+JAKmim8VWU8RXKZBpl0lLI7J8LijM0/q7oP7emfB6QMZV1Az+JgqGKSLpF0tYaav+KUVFQroZUxQTHA==",
+ "dev": true,
+ "dependencies": {
+ "jasmine-core": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "karma": "^6.0.0"
+ }
+ },
+ "node_modules/karma-source-map-support": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
+ "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
+ "dev": true,
+ "dependencies": {
+ "source-map-support": "^0.5.5"
+ }
+ },
+ "node_modules/karma-sourcemap-loader": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz",
+ "integrity": "sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2"
+ }
+ },
+ "node_modules/karma-webpack": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz",
+ "integrity": "sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3",
+ "minimatch": "^3.0.4",
+ "webpack-merge": "^4.1.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/karma-webpack/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/karma-webpack/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/karma-webpack/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/karma-webpack/node_modules/webpack-merge": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
+ "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "node_modules/karma/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/karma/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/karma/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/karma/node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/karma/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/karma/node_modules/tmp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+ "dev": true,
+ "dependencies": {
+ "rimraf": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.17.0"
+ }
+ },
+ "node_modules/karma/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/karma/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/klona": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
+ "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/kotlin": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/kotlin/-/kotlin-1.8.10.tgz",
+ "integrity": "sha512-vNB/4k1sv10L2GXasZwesZIV3pyEtB9MRmWyxXV4VsFD3KIJwQt81fcU2mXj/aGwKnL67zeMQpbgKU3yvfJpCw=="
+ },
+ "node_modules/kotlin-compiler": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/kotlin-compiler/-/kotlin-compiler-1.8.10.tgz",
+ "integrity": "sha512-jvmEVUb0M+2kiaytLI/QIYQT1rSh5VQs62ArebSDKMu2EO7ZRatUh/cTqR7QtLlsjzI8PV156BiOIn+CmoR+2A==",
+ "bin": {
+ "kotlinc-js": "bin/kotlinc-js-runner.js"
+ }
+ },
+ "node_modules/less": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
+ "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==",
+ "dev": true,
+ "dependencies": {
+ "copy-anything": "^2.0.1",
+ "parse-node-version": "^1.0.1",
+ "tslib": "^2.3.0"
+ },
+ "bin": {
+ "lessc": "bin/lessc"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "optionalDependencies": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
+ "mime": "^1.4.1",
+ "needle": "^3.1.0",
+ "source-map": "~0.6.0"
+ }
+ },
+ "node_modules/less-loader": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz",
+ "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==",
+ "dev": true,
+ "dependencies": {
+ "klona": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "less": "^3.5.0 || ^4.0.0",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/less/node_modules/make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/less/node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/less/node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/less/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/less/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/license-webpack-plugin": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
+ "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==",
+ "dev": true,
+ "dependencies": {
+ "webpack-sources": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack": {
+ "optional": true
+ },
+ "webpack-sources": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/limiter": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+ "engines": {
+ "node": ">=6.11.5"
+ }
+ },
+ "node_modules/loader-utils": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "node_modules/lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/log-symbols/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log4js": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.1.tgz",
+ "integrity": "sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ==",
+ "dev": true,
+ "dependencies": {
+ "date-format": "^4.0.14",
+ "debug": "^4.3.4",
+ "flatted": "^3.2.7",
+ "rfdc": "^1.3.0",
+ "streamroller": "^3.1.3"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/lower-case-first": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz",
+ "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==",
+ "dependencies": {
+ "lower-case": "^1.1.2"
+ }
+ },
+ "node_modules/lower-case-first/node_modules/lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lru-memoizer": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz",
+ "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==",
+ "dependencies": {
+ "lodash.clonedeep": "^4.5.0",
+ "lru-cache": "~4.0.0"
+ }
+ },
+ "node_modules/lru-memoizer/node_modules/lru-cache": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
+ "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==",
+ "dependencies": {
+ "pseudomap": "^1.0.1",
+ "yallist": "^2.0.0"
+ }
+ },
+ "node_modules/lru-memoizer/node_modules/yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
+ },
+ "node_modules/madge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/madge/-/madge-5.0.1.tgz",
+ "integrity": "sha512-krmSWL9Hkgub74bOjnjWRoFPAJvPwSG6Dbta06qhWOq6X/n/FPzO3ESZvbFYVIvG2g4UHXvCJN1b+RZLaSs9nA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.1",
+ "commander": "^7.2.0",
+ "commondir": "^1.0.1",
+ "debug": "^4.3.1",
+ "dependency-tree": "^8.1.1",
+ "detective-amd": "^3.1.0",
+ "detective-cjs": "^3.1.1",
+ "detective-es6": "^2.2.0",
+ "detective-less": "^1.0.2",
+ "detective-postcss": "^5.0.0",
+ "detective-sass": "^3.0.1",
+ "detective-scss": "^2.0.1",
+ "detective-stylus": "^1.0.0",
+ "detective-typescript": "^7.0.0",
+ "graphviz": "0.0.9",
+ "ora": "^5.4.1",
+ "pluralize": "^8.0.0",
+ "precinct": "^8.1.0",
+ "pretty-ms": "^7.0.1",
+ "rc": "^1.2.7",
+ "typescript": "^3.9.5",
+ "walkdir": "^0.4.1"
+ },
+ "bin": {
+ "madge": "bin/cli.js"
+ },
+ "engines": {
+ "node": "^10.13 || ^12 || >=14"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://www.paypal.me/pahen"
+ }
+ },
+ "node_modules/madge/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/madge/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/madge/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/madge/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/madge/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/madge/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/madge/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/madge/node_modules/typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
+ "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
+ "dev": true,
+ "dependencies": {
+ "sourcemap-codec": "^1.4.8"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/make-fetch-happen": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
+ "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==",
+ "dev": true,
+ "dependencies": {
+ "agentkeepalive": "^4.1.3",
+ "cacache": "^15.2.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.3",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^1.3.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.2",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^6.0.0",
+ "ssri": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/@npmcli/fs": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
+ "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==",
+ "dev": true,
+ "dependencies": {
+ "@gar/promisify": "^1.0.1",
+ "semver": "^7.3.5"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/@npmcli/move-file": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
+ "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
+ "deprecated": "This functionality has been moved to @npmcli/fs",
+ "dev": true,
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/cacache": {
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
+ "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==",
+ "dev": true,
+ "dependencies": {
+ "@npmcli/fs": "^1.0.0",
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "glob": "^7.1.4",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.1",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.2",
+ "mkdirp": "^1.0.3",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.1",
+ "tar": "^6.0.2",
+ "unique-filename": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/ssri": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
+ "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/make-fetch-happen/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/memfs": {
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz",
+ "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==",
+ "dev": true,
+ "dependencies": {
+ "fs-monkey": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
+ "dev": true
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mini-css-extract-plugin": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz",
+ "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==",
+ "dev": true,
+ "dependencies": {
+ "schema-utils": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
+ "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-collect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-fetch": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz",
+ "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.1.0",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.12"
+ }
+ },
+ "node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-json-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
+ "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
+ "dev": true,
+ "dependencies": {
+ "jsonparse": "^1.3.1",
+ "minipass": "^3.0.0"
+ }
+ },
+ "node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-sized": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+ "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minizlib/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/module-definition": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-3.4.0.tgz",
+ "integrity": "sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^3.0.0",
+ "node-source-walk": "^4.0.0"
+ },
+ "bin": {
+ "module-definition": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/module-lookup-amd": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz",
+ "integrity": "sha512-w9mCNlj0S8qviuHzpakaLVc+/7q50jl9a/kmJ/n8bmXQZgDPkQHnPBb8MUOYh3WpAYkXuNc2c+khsozhIp/amQ==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.8.1",
+ "debug": "^4.1.0",
+ "glob": "^7.1.6",
+ "requirejs": "^2.3.5",
+ "requirejs-config-file": "^4.0.0"
+ },
+ "bin": {
+ "lookup-amd": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/module-lookup-amd/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/module-lookup-amd/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/module-lookup-amd/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/module-lookup-amd/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/multicast-dns": {
+ "version": "7.2.5",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
+ "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
+ "dev": true,
+ "dependencies": {
+ "dns-packet": "^5.2.2",
+ "thunky": "^1.0.2"
+ },
+ "bin": {
+ "multicast-dns": "cli.js"
+ }
+ },
+ "node_modules/mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/natural-compare-lite": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+ "dev": true
+ },
+ "node_modules/needle": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz",
+ "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "debug": "^3.2.6",
+ "iconv-lite": "^0.6.3",
+ "sax": "^1.2.4"
+ },
+ "bin": {
+ "needle": "bin/needle"
+ },
+ "engines": {
+ "node": ">= 4.4.x"
+ }
+ },
+ "node_modules/needle/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/needle/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
+ "node_modules/nice-napi": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
+ "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "!win32"
+ ],
+ "dependencies": {
+ "node-addon-api": "^3.0.0",
+ "node-gyp-build": "^4.2.2"
+ }
+ },
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/node-forge": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/node-gyp": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
+ "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
+ "dev": true,
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^9.1.0",
+ "nopt": "^5.0.0",
+ "npmlog": "^6.0.0",
+ "rimraf": "^3.0.2",
+ "semver": "^7.3.5",
+ "tar": "^6.1.2",
+ "which": "^2.0.2"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": ">= 10.12.0"
+ }
+ },
+ "node_modules/node-gyp-build": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
+ "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
+ "node_modules/node-gyp/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/node-gyp/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/node-gyp/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
+ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A=="
+ },
+ "node_modules/node-source-walk": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-4.3.0.tgz",
+ "integrity": "sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/normalize-package-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz",
+ "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==",
+ "dev": true,
+ "dependencies": {
+ "hosted-git-info": "^5.0.0",
+ "is-core-module": "^2.8.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-bundled": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
+ "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
+ "dev": true,
+ "dependencies": {
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "node_modules/npm-install-checks": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz",
+ "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.1.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-normalize-package-bin": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+ "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+ "dev": true
+ },
+ "node_modules/npm-package-arg": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.2.tgz",
+ "integrity": "sha512-v/miORuX8cndiOheW8p2moNuPJ7QhcFh9WGlTorruG8hXSA23vMTEp5hTCmDxic0nD8KHhj/NQgFuySD3GYY3g==",
+ "dev": true,
+ "dependencies": {
+ "hosted-git-info": "^5.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-packlist": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz",
+ "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^8.0.1",
+ "ignore-walk": "^5.0.1",
+ "npm-bundled": "^2.0.0",
+ "npm-normalize-package-bin": "^2.0.0"
+ },
+ "bin": {
+ "npm-packlist": "bin/index.js"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-packlist/node_modules/npm-bundled": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz",
+ "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==",
+ "dev": true,
+ "dependencies": {
+ "npm-normalize-package-bin": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz",
+ "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-pick-manifest": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz",
+ "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==",
+ "dev": true,
+ "dependencies": {
+ "npm-install-checks": "^5.0.0",
+ "npm-normalize-package-bin": "^1.0.1",
+ "npm-package-arg": "^9.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz",
+ "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==",
+ "dev": true,
+ "dependencies": {
+ "make-fetch-happen": "^10.0.6",
+ "minipass": "^3.1.6",
+ "minipass-fetch": "^2.0.3",
+ "minipass-json-stream": "^1.0.1",
+ "minizlib": "^2.1.2",
+ "npm-package-arg": "^9.0.1",
+ "proc-log": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
+ "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
+ "dev": true,
+ "dependencies": {
+ "agentkeepalive": "^4.2.1",
+ "cacache": "^16.1.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^2.0.3",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^7.0.0",
+ "ssri": "^9.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/minipass-fetch": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
+ "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.1.6",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.13"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/socks-proxy-agent": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+ "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npmlog": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+ "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+ "dev": true,
+ "dependencies": {
+ "are-we-there-yet": "^3.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^4.0.3",
+ "set-blocking": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
+ "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+ "dev": true,
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ora/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/ora/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pacote": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.3.0.tgz",
+ "integrity": "sha512-auhJAUlfC2TALo6I0s1vFoPvVFgWGx+uz/PnIojTTgkGwlK3Np8sGJ0ghfFhiuzJXTZoTycMLk8uLskdntPbDw==",
+ "dev": true,
+ "dependencies": {
+ "@npmcli/git": "^3.0.0",
+ "@npmcli/installed-package-contents": "^1.0.7",
+ "@npmcli/promise-spawn": "^3.0.0",
+ "@npmcli/run-script": "^3.0.1",
+ "cacache": "^16.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "infer-owner": "^1.0.4",
+ "minipass": "^3.1.6",
+ "mkdirp": "^1.0.4",
+ "npm-package-arg": "^9.0.0",
+ "npm-packlist": "^5.0.0",
+ "npm-pick-manifest": "^7.0.0",
+ "npm-registry-fetch": "^13.0.1",
+ "proc-log": "^2.0.0",
+ "promise-retry": "^2.0.1",
+ "read-package-json": "^5.0.0",
+ "read-package-json-fast": "^2.0.3",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11"
+ },
+ "bin": {
+ "pacote": "lib/bin.js"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "node_modules/param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
+ "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-node-version": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "optional": true
+ },
+ "node_modules/parse5-html-rewriting-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz",
+ "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==",
+ "dev": true,
+ "dependencies": {
+ "parse5": "^6.0.1",
+ "parse5-sax-parser": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-html-rewriting-stream/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "dev": true,
+ "dependencies": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "node_modules/parse5-sax-parser": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz",
+ "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==",
+ "dev": true,
+ "dependencies": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-sax-parser/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/path-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz",
+ "integrity": "sha512-2snAGA6xVRqTuTPa40bn0iEpYtVK6gEqeyS/63dqpm5pGlesOv6EmRcnB9Rr6eAnAC2Wqlbz0tqgJZryttxhxg==",
+ "dependencies": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
+ "dev": true
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "dependencies": {
+ "pinkie": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/piscina": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz",
+ "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==",
+ "dev": true,
+ "dependencies": {
+ "eventemitter-asyncresource": "^1.0.0",
+ "hdr-histogram-js": "^2.0.1",
+ "hdr-histogram-percentiles-obj": "^3.0.0"
+ },
+ "optionalDependencies": {
+ "nice-napi": "^1.0.2"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.16",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
+ "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-attribute-case-insensitive": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz",
+ "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-clamp": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz",
+ "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=7.6.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.6"
+ }
+ },
+ "node_modules/postcss-color-functional-notation": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz",
+ "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-color-hex-alpha": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz",
+ "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-rebeccapurple": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz",
+ "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-custom-media": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz",
+ "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ }
+ },
+ "node_modules/postcss-custom-properties": {
+ "version": "12.1.11",
+ "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz",
+ "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-custom-selectors": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz",
+ "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.4"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ }
+ },
+ "node_modules/postcss-dir-pseudo-class": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz",
+ "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-double-position-gradients": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz",
+ "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-env-function": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz",
+ "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-visible": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz",
+ "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-within": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz",
+ "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-font-variant": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz",
+ "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==",
+ "dev": true,
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-gap-properties": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz",
+ "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==",
+ "dev": true,
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-image-set-function": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz",
+ "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz",
+ "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-initial": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz",
+ "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==",
+ "dev": true,
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-lab-function": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz",
+ "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-loader": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz",
+ "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==",
+ "dev": true,
+ "dependencies": {
+ "cosmiconfig": "^7.0.0",
+ "klona": "^2.0.5",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "postcss": "^7.0.0 || ^8.0.1",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/postcss-logical": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz",
+ "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==",
+ "dev": true,
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-media-minmax": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz",
+ "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-extract-imports": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
+ "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
+ "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-scope": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
+ "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.4"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-nesting": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz",
+ "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/selector-specificity": "^2.0.0",
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-opacity-percentage": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz",
+ "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "kofi",
+ "url": "https://ko-fi.com/mrcgrtz"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mrcgrtz"
+ }
+ ],
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-overflow-shorthand": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz",
+ "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-page-break": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz",
+ "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==",
+ "dev": true,
+ "peerDependencies": {
+ "postcss": "^8"
+ }
+ },
+ "node_modules/postcss-place": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz",
+ "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-preset-env": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz",
+ "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==",
+ "dev": true,
+ "dependencies": {
+ "@csstools/postcss-cascade-layers": "^1.0.5",
+ "@csstools/postcss-color-function": "^1.1.1",
+ "@csstools/postcss-font-format-keywords": "^1.0.1",
+ "@csstools/postcss-hwb-function": "^1.0.2",
+ "@csstools/postcss-ic-unit": "^1.0.1",
+ "@csstools/postcss-is-pseudo-class": "^2.0.7",
+ "@csstools/postcss-nested-calc": "^1.0.0",
+ "@csstools/postcss-normalize-display-values": "^1.0.1",
+ "@csstools/postcss-oklab-function": "^1.1.1",
+ "@csstools/postcss-progressive-custom-properties": "^1.3.0",
+ "@csstools/postcss-stepped-value-functions": "^1.0.1",
+ "@csstools/postcss-text-decoration-shorthand": "^1.0.0",
+ "@csstools/postcss-trigonometric-functions": "^1.0.2",
+ "@csstools/postcss-unset-value": "^1.0.2",
+ "autoprefixer": "^10.4.8",
+ "browserslist": "^4.21.3",
+ "css-blank-pseudo": "^3.0.3",
+ "css-has-pseudo": "^3.0.4",
+ "css-prefers-color-scheme": "^6.0.3",
+ "cssdb": "^7.0.0",
+ "postcss-attribute-case-insensitive": "^5.0.2",
+ "postcss-clamp": "^4.1.0",
+ "postcss-color-functional-notation": "^4.2.4",
+ "postcss-color-hex-alpha": "^8.0.4",
+ "postcss-color-rebeccapurple": "^7.1.1",
+ "postcss-custom-media": "^8.0.2",
+ "postcss-custom-properties": "^12.1.8",
+ "postcss-custom-selectors": "^6.0.3",
+ "postcss-dir-pseudo-class": "^6.0.5",
+ "postcss-double-position-gradients": "^3.1.2",
+ "postcss-env-function": "^4.0.6",
+ "postcss-focus-visible": "^6.0.4",
+ "postcss-focus-within": "^5.0.4",
+ "postcss-font-variant": "^5.0.0",
+ "postcss-gap-properties": "^3.0.5",
+ "postcss-image-set-function": "^4.0.7",
+ "postcss-initial": "^4.0.1",
+ "postcss-lab-function": "^4.2.1",
+ "postcss-logical": "^5.0.4",
+ "postcss-media-minmax": "^5.0.0",
+ "postcss-nesting": "^10.1.10",
+ "postcss-opacity-percentage": "^1.1.2",
+ "postcss-overflow-shorthand": "^3.0.4",
+ "postcss-page-break": "^3.0.4",
+ "postcss-place": "^7.0.5",
+ "postcss-pseudo-class-any-link": "^7.1.6",
+ "postcss-replace-overflow-wrap": "^4.0.0",
+ "postcss-selector-not": "^6.0.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-pseudo-class-any-link": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz",
+ "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-replace-overflow-wrap": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz",
+ "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==",
+ "dev": true,
+ "peerDependencies": {
+ "postcss": "^8.0.3"
+ }
+ },
+ "node_modules/postcss-selector-not": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz",
+ "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.11",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
+ "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "node_modules/postcss-values-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-5.0.0.tgz",
+ "integrity": "sha512-2viDDjMMrt21W2izbeiJxl3kFuD/+asgB0CBwPEgSyhCmBnDIa/y+pLaoyX+q3I3DHH0oPPL3cgjVTQvlS1Maw==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "^1.1.4",
+ "is-url-superb": "^4.0.0",
+ "quote-unquote": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
+ },
+ "node_modules/postcss-values-parser/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/precinct": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/precinct/-/precinct-8.3.1.tgz",
+ "integrity": "sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.20.3",
+ "debug": "^4.3.3",
+ "detective-amd": "^3.1.0",
+ "detective-cjs": "^3.1.1",
+ "detective-es6": "^2.2.1",
+ "detective-less": "^1.0.2",
+ "detective-postcss": "^4.0.0",
+ "detective-sass": "^3.0.1",
+ "detective-scss": "^2.0.1",
+ "detective-stylus": "^1.0.0",
+ "detective-typescript": "^7.0.0",
+ "module-definition": "^3.3.1",
+ "node-source-walk": "^4.2.0"
+ },
+ "bin": {
+ "precinct": "bin/cli.js"
+ },
+ "engines": {
+ "node": "^10.13 || ^12 || >=14"
+ }
+ },
+ "node_modules/precinct/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/precinct/node_modules/detective-postcss": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-4.0.0.tgz",
+ "integrity": "sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "is-url": "^1.2.4",
+ "postcss": "^8.1.7",
+ "postcss-values-parser": "^2.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/precinct/node_modules/postcss-values-parser": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz",
+ "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==",
+ "dev": true,
+ "dependencies": {
+ "flatten": "^1.0.2",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=6.14.4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz",
+ "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/prettier-plugin-organize-imports": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.1.tgz",
+ "integrity": "sha512-bty7C2Ecard5EOXirtzeCAqj4FU4epeuWrQt/Z+sh8UVEpBlBZ3m3KNPz2kFu7KgRTQx/C9o4/TdquPD1jOqjQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@volar/vue-language-plugin-pug": "^1.0.4",
+ "@volar/vue-typescript": "^1.0.4",
+ "prettier": ">=2.0",
+ "typescript": ">=2.9"
+ },
+ "peerDependenciesMeta": {
+ "@volar/vue-language-plugin-pug": {
+ "optional": true
+ },
+ "@volar/vue-typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pretty-error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+ "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+ "dependencies": {
+ "lodash": "^4.17.20",
+ "renderkid": "^3.0.0"
+ }
+ },
+ "node_modules/pretty-ms": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
+ "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
+ "dev": true,
+ "dependencies": {
+ "parse-ms": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/proc-log": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
+ "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==",
+ "dev": true,
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "node_modules/promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+ "dev": true
+ },
+ "node_modules/promise-polyfill": {
+ "version": "8.2.3",
+ "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz",
+ "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg=="
+ },
+ "node_modules/promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "dev": true,
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/promise-retry/node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "6.11.3",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
+ "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ },
+ "bin": {
+ "pbjs": "bin/pbjs",
+ "pbts": "bin/pbts"
+ }
+ },
+ "node_modules/protractor": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz",
+ "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==",
+ "deprecated": "We have news to share - Protractor is deprecated and will reach end-of-life by Summer 2023. To learn more and find out about other options please refer to this post on the Angular blog. Thank you for using and contributing to Protractor. https://goo.gle/state-of-e2e-in-angular",
+ "dev": true,
+ "dependencies": {
+ "@types/q": "^0.0.32",
+ "@types/selenium-webdriver": "^3.0.0",
+ "blocking-proxy": "^1.0.0",
+ "browserstack": "^1.5.1",
+ "chalk": "^1.1.3",
+ "glob": "^7.0.3",
+ "jasmine": "2.8.0",
+ "jasminewd2": "^2.1.0",
+ "q": "1.4.1",
+ "saucelabs": "^1.5.0",
+ "selenium-webdriver": "3.6.0",
+ "source-map-support": "~0.4.0",
+ "webdriver-js-extender": "2.1.0",
+ "webdriver-manager": "^12.1.7",
+ "yargs": "^15.3.1"
+ },
+ "bin": {
+ "protractor": "bin/protractor",
+ "webdriver-manager": "bin/webdriver-manager"
+ },
+ "engines": {
+ "node": ">=10.13.x"
+ }
+ },
+ "node_modules/protractor/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/protractor/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/protractor/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/protractor/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/protractor/node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/protractor/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/protractor/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/protractor/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/protractor/node_modules/jasmine": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
+ "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==",
+ "dev": true,
+ "dependencies": {
+ "exit": "^0.1.2",
+ "glob": "^7.0.6",
+ "jasmine-core": "~2.8.0"
+ },
+ "bin": {
+ "jasmine": "bin/jasmine.js"
+ }
+ },
+ "node_modules/protractor/node_modules/jasmine-core": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
+ "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==",
+ "dev": true
+ },
+ "node_modules/protractor/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/protractor/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/protractor/node_modules/source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "dependencies": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "node_modules/protractor/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/protractor/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/protractor/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/protractor/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "node_modules/protractor/node_modules/yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/protractor/node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-addr/node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+ },
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/q": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+ "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.0",
+ "teleport": ">=0.2.0"
+ }
+ },
+ "node_modules/qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.9"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/quote-unquote": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz",
+ "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==",
+ "dev": true
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/read-package-json": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz",
+ "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^8.0.1",
+ "json-parse-even-better-errors": "^2.3.1",
+ "normalize-package-data": "^4.0.0",
+ "npm-normalize-package-bin": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/read-package-json-fast": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz",
+ "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==",
+ "dev": true,
+ "dependencies": {
+ "json-parse-even-better-errors": "^2.3.0",
+ "npm-normalize-package-bin": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/read-package-json/node_modules/npm-normalize-package-bin": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz",
+ "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+ "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+ "dependencies": {
+ "resolve": "^1.9.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/reflect-metadata": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+ "dev": true
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
+ "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.9",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
+ "dev": true
+ },
+ "node_modules/regenerator-transform": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
+ "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.8.4"
+ }
+ },
+ "node_modules/regex-parser": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
+ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
+ "dev": true
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz",
+ "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.1.0",
+ "regjsgen": "^0.7.1",
+ "regjsparser": "^0.9.1",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regjsgen": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz",
+ "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==",
+ "dev": true
+ },
+ "node_modules/regjsparser": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
+ "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+ "dev": true,
+ "dependencies": {
+ "jsesc": "~0.5.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/regjsparser/node_modules/jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ }
+ },
+ "node_modules/relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/renderkid": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+ "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+ "dependencies": {
+ "css-select": "^4.1.3",
+ "dom-converter": "^0.2.0",
+ "htmlparser2": "^6.1.0",
+ "lodash": "^4.17.21",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "node_modules/request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
+ "dev": true,
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/request/node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/request/node_modules/qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/request/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "node_modules/requirejs": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
+ "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
+ "dev": true,
+ "bin": {
+ "r_js": "bin/r.js",
+ "r.js": "bin/r.js"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/requirejs-config-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz",
+ "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.0",
+ "stringify-object": "^3.2.1"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "node_modules/resolve": {
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+ "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+ "dependencies": {
+ "is-core-module": "^2.8.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-dependency-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz",
+ "integrity": "sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-url-loader": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz",
+ "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==",
+ "dev": true,
+ "dependencies": {
+ "adjust-sourcemap-loader": "^4.0.0",
+ "convert-source-map": "^1.7.0",
+ "loader-utils": "^2.0.0",
+ "postcss": "^8.2.14",
+ "source-map": "0.6.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/resolve-url-loader/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rest-facade": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/rest-facade/-/rest-facade-1.16.3.tgz",
+ "integrity": "sha512-9BQTPLiwg23XZwcWi0ys1wTizfc//0b2G3U6vBWcgqh56ozs2K6CD+Jw4DYcw3AqdPQN7jj8nzRUcUXFVGzb0Q==",
+ "dependencies": {
+ "change-case": "^2.3.0",
+ "deepmerge": "^3.2.0",
+ "lodash.get": "^4.4.2",
+ "superagent": "^5.1.1"
+ },
+ "peerDependencies": {
+ "superagent-proxy": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "superagent-proxy": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+ "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
+ "dev": true
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/run-async": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.5.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz",
+ "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/sass": {
+ "version": "1.57.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
+ "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/sass-loader": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz",
+ "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==",
+ "dev": true,
+ "dependencies": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "fibers": ">= 3.1.0",
+ "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "sass": "^1.3.0",
+ "sass-embedded": "*",
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "fibers": {
+ "optional": true
+ },
+ "node-sass": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/sass-lookup": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-3.0.0.tgz",
+ "integrity": "sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.16.0"
+ },
+ "bin": {
+ "sass-lookup": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/sass-lookup/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/saucelabs": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
+ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
+ "dev": true,
+ "dependencies": {
+ "https-proxy-agent": "^2.2.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/saucelabs/node_modules/agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "dependencies": {
+ "es6-promisify": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/saucelabs/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/saucelabs/node_modules/https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "node_modules/schema-utils": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+ "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.5",
+ "ajv": "^6.12.4",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 8.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/schema-utils/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/schema-utils/node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/schema-utils/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
+ "dev": true
+ },
+ "node_modules/selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "dependencies": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ },
+ "engines": {
+ "node": ">= 6.9.0"
+ }
+ },
+ "node_modules/selenium-webdriver/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/selenium-webdriver/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/selenium-webdriver/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/selenium-webdriver/node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/selenium-webdriver/node_modules/tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==",
+ "dev": true,
+ "dependencies": {
+ "os-tmpdir": "~1.0.1"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/selfsigned": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz",
+ "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==",
+ "dev": true,
+ "dependencies": {
+ "node-forge": "^1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/send/node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/send/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/sentence-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz",
+ "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==",
+ "dependencies": {
+ "lower-case": "^1.1.1"
+ }
+ },
+ "node_modules/sentence-case/node_modules/lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/serve-index/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/serve-index/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-index/node_modules/http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "dev": true,
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-index/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true
+ },
+ "node_modules/serve-index/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/serve-index/node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "node_modules/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "dev": true,
+ "dependencies": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.18.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/snake-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz",
+ "integrity": "sha512-oapUKC+qulnUIN+/O7Tbl2msi9PQvJeivGN9RNbygxzI2EOY0gA96i8BJLYnGUWSLGcYtyW4YYqnGTZEySU/gg==",
+ "dependencies": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "node_modules/socket.io": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz",
+ "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "debug": "~4.3.2",
+ "engine.io": "~6.2.1",
+ "socket.io-adapter": "~2.4.0",
+ "socket.io-parser": "~4.2.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-adapter": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
+ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
+ "dev": true
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
+ "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
+ "dev": true,
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/sockjs": {
+ "version": "0.3.24",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+ "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+ "dev": true,
+ "dependencies": {
+ "faye-websocket": "^0.11.3",
+ "uuid": "^8.3.2",
+ "websocket-driver": "^0.7.4"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
+ "dev": true,
+ "dependencies": {
+ "ip": "^2.0.0",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz",
+ "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-loader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz",
+ "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.6",
+ "iconv-lite": "^0.6.3",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.72.1"
+ }
+ },
+ "node_modules/source-map-loader/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "deprecated": "See https://github.com/lydell/source-map-url#deprecated"
+ },
+ "node_modules/sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "deprecated": "Please use @jridgewell/sourcemap-codec instead",
+ "dev": true
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
+ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==",
+ "dev": true
+ },
+ "node_modules/spdy": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/spdy-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sshpk/node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ },
+ "node_modules/ssri": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
+ "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/streamroller": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.3.tgz",
+ "integrity": "sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w==",
+ "dev": true,
+ "dependencies": {
+ "date-format": "^4.0.14",
+ "debug": "^4.3.4",
+ "fs-extra": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/stringify-object": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
+ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+ "dev": true,
+ "dependencies": {
+ "get-own-enumerable-property-symbols": "^3.0.0",
+ "is-obj": "^1.0.1",
+ "is-regexp": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/style-loader": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
+ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/stylus": {
+ "version": "0.59.0",
+ "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz",
+ "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==",
+ "dev": true,
+ "dependencies": {
+ "@adobe/css-tools": "^4.0.1",
+ "debug": "^4.3.2",
+ "glob": "^7.1.6",
+ "sax": "~1.2.4",
+ "source-map": "^0.7.3"
+ },
+ "bin": {
+ "stylus": "bin/stylus"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://opencollective.com/stylus"
+ }
+ },
+ "node_modules/stylus-loader": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz",
+ "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==",
+ "dev": true,
+ "dependencies": {
+ "fast-glob": "^3.2.11",
+ "klona": "^2.0.5",
+ "normalize-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "stylus": ">=0.52.4",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/stylus-lookup": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-3.0.2.tgz",
+ "integrity": "sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.8.1",
+ "debug": "^4.1.0"
+ },
+ "bin": {
+ "stylus-lookup": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/stylus-lookup/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/stylus/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/stylus/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/stylus/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/superagent": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz",
+ "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==",
+ "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at <https://github.com/visionmedia/superagent/releases>.",
+ "dependencies": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.2",
+ "debug": "^4.1.1",
+ "fast-safe-stringify": "^2.0.7",
+ "form-data": "^3.0.0",
+ "formidable": "^1.2.2",
+ "methods": "^1.1.2",
+ "mime": "^2.4.6",
+ "qs": "^6.9.4",
+ "readable-stream": "^3.6.0",
+ "semver": "^7.3.2"
+ },
+ "engines": {
+ "node": ">= 7.0.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/swap-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz",
+ "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==",
+ "dependencies": {
+ "lower-case": "^1.1.1",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "node_modules/swap-case/node_modules/lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ },
+ "node_modules/symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.1.13",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
+ "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==",
+ "dev": true,
+ "dependencies": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^4.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar/node_modules/minipass": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz",
+ "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/temp": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.4.0.tgz",
+ "integrity": "sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA==",
+ "dev": true,
+ "engines": [
+ "node >=0.4.0"
+ ]
+ },
+ "node_modules/terser": {
+ "version": "5.14.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
+ "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
+ "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.14",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^3.1.1",
+ "serialize-javascript": "^6.0.0",
+ "terser": "^5.14.1"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "uglify-js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/test-exclude/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/test-exclude/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/test-exclude/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/text-segmentation": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
+ "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+ "dependencies": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/three": {
+ "version": "0.143.0",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz",
+ "integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg=="
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "node_modules/thunky": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+ "dev": true
+ },
+ "node_modules/title-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz",
+ "integrity": "sha512-xYbo5Um5MBgn24xJSK+x5hZ8ehuGXTVhgx32KJCThHRHwpyIb1lmABi1DH5VvN9E7rNEquPjz//rF/tZQd7mjQ==",
+ "dependencies": {
+ "sentence-case": "^1.1.1",
+ "upper-case": "^1.0.3"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/ts-loader": {
+ "version": "9.4.2",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
+ "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "enhanced-resolve": "^5.0.0",
+ "micromatch": "^4.0.0",
+ "semver": "^7.3.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "typescript": "*",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/ts-loader/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ts-loader/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ts-loader/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ts-loader/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/ts-loader/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ts-loader/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+ "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
+ },
+ "node_modules/tslint": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+ "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
+ "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.3",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.13.0",
+ "tsutils": "^2.29.0"
+ },
+ "bin": {
+ "tslint": "bin/tslint"
+ },
+ "engines": {
+ "node": ">=4.8.0"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev"
+ }
+ },
+ "node_modules/tslint/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/tslint/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/tslint/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/tslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tslint/node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/tslint/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/tslint/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/tslint/node_modules/tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev"
+ }
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/tsutils/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typed-assert": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz",
+ "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==",
+ "dev": true
+ },
+ "node_modules/typescript": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/ua-parser-js": {
+ "version": "0.7.32",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
+ "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ua-parser-js"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/faisalman"
+ }
+ ],
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/unfetch": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
+ "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA=="
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==",
+ "dev": true
+ },
+ "node_modules/unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "dependencies": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "node_modules/unique-slug": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+ "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "browserslist-lint": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/upper-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+ "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA=="
+ },
+ "node_modules/upper-case-first": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz",
+ "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==",
+ "dependencies": {
+ "upper-case": "^1.1.1"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/utila": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+ "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/utrie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
+ "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+ "dependencies": {
+ "base64-arraybuffer": "^1.0.2"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/validate-npm-package-name": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz",
+ "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==",
+ "dev": true,
+ "dependencies": {
+ "builtins": "^5.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/walkdir": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+ "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/watchpack": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
+ "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "dependencies": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/webdriver-js-extender": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
+ "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/selenium-webdriver": "^3.0.0",
+ "selenium-webdriver": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=6.9.x"
+ }
+ },
+ "node_modules/webdriver-manager": {
+ "version": "12.1.8",
+ "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz",
+ "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==",
+ "dev": true,
+ "dependencies": {
+ "adm-zip": "^0.4.9",
+ "chalk": "^1.1.1",
+ "del": "^2.2.0",
+ "glob": "^7.0.3",
+ "ini": "^1.3.4",
+ "minimist": "^1.2.0",
+ "q": "^1.4.1",
+ "request": "^2.87.0",
+ "rimraf": "^2.5.2",
+ "semver": "^5.3.0",
+ "xml2js": "^0.4.17"
+ },
+ "bin": {
+ "webdriver-manager": "bin/webdriver-manager"
+ },
+ "engines": {
+ "node": ">=6.9.x"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/webdriver-manager/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webdriver-manager/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/webgl-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/webgl-utils/-/webgl-utils-1.0.1.tgz",
+ "integrity": "sha512-ox5xQ3YkrrOR6pCZHTOud49zzMXP9LnXzx7jIQvHOinV4FK59rGGJURw2Lq1cCTPgMVU2wAWq7e6vEVjz9FdBw=="
+ },
+ "node_modules/webgl-utils.js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/webgl-utils.js/-/webgl-utils.js-1.1.0.tgz",
+ "integrity": "sha512-cmO2aPd6gR6bK/ttdk8ZIypJfZMOcTvsvXv/LxXZjAFu5TC6vXqFrZYudlPuKxVsA34Pc8Fysq2rCnflu+wuuA=="
+ },
+ "node_modules/webpack": {
+ "version": "5.75.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
+ "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/wasm-edit": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.7.6",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.10.0",
+ "es-module-lexer": "^0.9.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.1.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.1.3",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
+ "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
+ "dependencies": {
+ "@discoveryjs/json-ext": "^0.5.0",
+ "@webpack-cli/configtest": "^1.2.0",
+ "@webpack-cli/info": "^1.5.0",
+ "@webpack-cli/serve": "^1.7.0",
+ "colorette": "^2.0.14",
+ "commander": "^7.0.0",
+ "cross-spawn": "^7.0.3",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^2.2.0",
+ "rechoir": "^0.7.0",
+ "webpack-merge": "^5.7.3"
+ },
+ "bin": {
+ "webpack-cli": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "4.x.x || 5.x.x"
+ },
+ "peerDependenciesMeta": {
+ "@webpack-cli/generators": {
+ "optional": true
+ },
+ "@webpack-cli/migrate": {
+ "optional": true
+ },
+ "webpack-bundle-analyzer": {
+ "optional": true
+ },
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/webpack-dev-middleware": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
+ "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
+ "dev": true,
+ "dependencies": {
+ "colorette": "^2.0.10",
+ "memfs": "^3.4.3",
+ "mime-types": "^2.1.31",
+ "range-parser": "^1.2.1",
+ "schema-utils": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/webpack-dev-middleware/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/webpack-dev-server": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz",
+ "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==",
+ "dev": true,
+ "dependencies": {
+ "@types/bonjour": "^3.5.9",
+ "@types/connect-history-api-fallback": "^1.3.5",
+ "@types/express": "^4.17.13",
+ "@types/serve-index": "^1.9.1",
+ "@types/serve-static": "^1.13.10",
+ "@types/sockjs": "^0.3.33",
+ "@types/ws": "^8.5.1",
+ "ansi-html-community": "^0.0.8",
+ "bonjour-service": "^1.0.11",
+ "chokidar": "^3.5.3",
+ "colorette": "^2.0.10",
+ "compression": "^1.7.4",
+ "connect-history-api-fallback": "^2.0.0",
+ "default-gateway": "^6.0.3",
+ "express": "^4.17.3",
+ "graceful-fs": "^4.2.6",
+ "html-entities": "^2.3.2",
+ "http-proxy-middleware": "^2.0.3",
+ "ipaddr.js": "^2.0.1",
+ "open": "^8.0.9",
+ "p-retry": "^4.5.0",
+ "rimraf": "^3.0.2",
+ "schema-utils": "^4.0.0",
+ "selfsigned": "^2.0.1",
+ "serve-index": "^1.9.1",
+ "sockjs": "^0.3.24",
+ "spdy": "^4.0.2",
+ "webpack-dev-middleware": "^5.3.1",
+ "ws": "^8.4.2"
+ },
+ "bin": {
+ "webpack-dev-server": "bin/webpack-dev-server.js"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.37.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+ "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack-subresource-integrity": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz",
+ "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==",
+ "dev": true,
+ "dependencies": {
+ "typed-assert": "^1.0.8"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "html-webpack-plugin": ">= 5.0.0-beta.1 < 6",
+ "webpack": "^5.12.0"
+ },
+ "peerDependenciesMeta": {
+ "html-webpack-plugin": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/webpack/node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/webpack/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "node_modules/webpack/node_modules/schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/ws": {
+ "version": "8.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+ "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dev": true,
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
+ "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zone.js": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz",
+ "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ }
+ }
+ },
+ "dependencies": {
+ "@adobe/css-tools": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz",
+ "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==",
+ "dev": true
+ },
+ "@ampproject/remapping": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+ "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@angular-devkit/architect": {
+ "version": "0.1402.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.10.tgz",
+ "integrity": "sha512-/6YmPrgataj1jD2Uqd1ED+CG4DaZGacoeZd/89hH7hF76Nno8K18DrSOqJAEmDnOWegpSRGVLd0qP09IHmaG5w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "14.2.10",
+ "rxjs": "6.6.7"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/build-angular": {
+ "version": "14.2.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.10.tgz",
+ "integrity": "sha512-VCeZAyq4uPCJukKInaSiD4i/GgxgcU4jFlLFQtoYNmaBS4xbPOymL19forRIihiV0dwNEa2L694vRTAPMBxIfw==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "2.2.0",
+ "@angular-devkit/architect": "0.1402.10",
+ "@angular-devkit/build-webpack": "0.1402.10",
+ "@angular-devkit/core": "14.2.10",
+ "@babel/core": "7.18.10",
+ "@babel/generator": "7.18.12",
+ "@babel/helper-annotate-as-pure": "7.18.6",
+ "@babel/plugin-proposal-async-generator-functions": "7.18.10",
+ "@babel/plugin-transform-async-to-generator": "7.18.6",
+ "@babel/plugin-transform-runtime": "7.18.10",
+ "@babel/preset-env": "7.18.10",
+ "@babel/runtime": "7.18.9",
+ "@babel/template": "7.18.10",
+ "@discoveryjs/json-ext": "0.5.7",
+ "@ngtools/webpack": "14.2.10",
+ "ansi-colors": "4.1.3",
+ "babel-loader": "8.2.5",
+ "babel-plugin-istanbul": "6.1.1",
+ "browserslist": "^4.9.1",
+ "cacache": "16.1.2",
+ "copy-webpack-plugin": "11.0.0",
+ "critters": "0.0.16",
+ "css-loader": "6.7.1",
+ "esbuild": "0.15.5",
+ "esbuild-wasm": "0.15.5",
+ "glob": "8.0.3",
+ "https-proxy-agent": "5.0.1",
+ "inquirer": "8.2.4",
+ "jsonc-parser": "3.1.0",
+ "karma-source-map-support": "1.4.0",
+ "less": "4.1.3",
+ "less-loader": "11.0.0",
+ "license-webpack-plugin": "4.0.2",
+ "loader-utils": "3.2.1",
+ "mini-css-extract-plugin": "2.6.1",
+ "minimatch": "5.1.0",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "parse5-html-rewriting-stream": "6.0.1",
+ "piscina": "3.2.0",
+ "postcss": "8.4.16",
+ "postcss-import": "15.0.0",
+ "postcss-loader": "7.0.1",
+ "postcss-preset-env": "7.8.0",
+ "regenerator-runtime": "0.13.9",
+ "resolve-url-loader": "5.0.0",
+ "rxjs": "6.6.7",
+ "sass": "1.54.4",
+ "sass-loader": "13.0.2",
+ "semver": "7.3.7",
+ "source-map-loader": "4.0.0",
+ "source-map-support": "0.5.21",
+ "stylus": "0.59.0",
+ "stylus-loader": "7.0.0",
+ "terser": "5.14.2",
+ "text-table": "0.2.0",
+ "tree-kill": "1.2.2",
+ "tslib": "2.4.0",
+ "webpack": "5.74.0",
+ "webpack-dev-middleware": "5.3.3",
+ "webpack-dev-server": "4.11.0",
+ "webpack-merge": "5.8.0",
+ "webpack-subresource-integrity": "5.1.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
+ "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "sass": {
+ "version": "1.54.4",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz",
+ "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
+ "sass-loader": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz",
+ "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==",
+ "dev": true,
+ "requires": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ }
+ },
+ "schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "dev": true
+ },
+ "webpack": {
+ "version": "5.74.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
+ "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
+ "dev": true,
+ "requires": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/wasm-edit": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.7.6",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.10.0",
+ "es-module-lexer": "^0.9.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.1.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.1.3",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ }
+ }
+ }
+ },
+ "@angular-devkit/build-webpack": {
+ "version": "0.1402.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.10.tgz",
+ "integrity": "sha512-h+2MaSY7QSvoJ3R+Hvin21jVCfPGOTLdASIUk4Jmq6J3y5BSku3KSSaV8dWoBOBkFCwQyPQMRjiHoHKLpC1K7g==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.1402.10",
+ "rxjs": "6.6.7"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "14.2.10",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.10.tgz",
+ "integrity": "sha512-K4AO7mROTdbhQ7chtyQd6oPwmuL+BPUh+wn6Aq1qrmYJK4UZYFOPp8fi/Ehs8meCEeywtrssOPfrOE4Gsre9dg==",
+ "dev": true,
+ "requires": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.1.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.4"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/schematics": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.0.7.tgz",
+ "integrity": "sha512-nJUJXCBQr7rmVn6IXFAXMCWAB1w6JQmFGuFVW0G3GH/A0e+A3ttzJc6qVLYluqaFoafw394cZu24YJo55E/+Zg==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "14.0.7",
+ "jsonc-parser": "3.0.0",
+ "magic-string": "0.26.1",
+ "ora": "5.4.1",
+ "rxjs": "6.6.7"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz",
+ "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==",
+ "dev": true,
+ "requires": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.0.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ }
+ },
+ "jsonc-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/animations": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.12.tgz",
+ "integrity": "sha512-gwdnFZkvVUr+enUNfhfCGRGGqNHn1+vTA81apLfHYhJxgjiLUtETc4KTOrQevtDm022pEd+LSrvr8r+7ag+jkw==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/cdk": {
+ "version": "14.2.7",
+ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz",
+ "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==",
+ "requires": {
+ "parse5": "^5.0.0",
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/cli": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.0.7.tgz",
+ "integrity": "sha512-tABt1EDwBHm0ngsutdkXXWgPgHzapGLC7rSPHXStMc24ngViFZpXGzBCpompjHvXNt6bjklmJmuRvjS6+ktBZA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.1400.7",
+ "@angular-devkit/core": "14.0.7",
+ "@angular-devkit/schematics": "14.0.7",
+ "@schematics/angular": "14.0.7",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ansi-colors": "4.1.1",
+ "debug": "4.3.4",
+ "ini": "3.0.0",
+ "inquirer": "8.2.4",
+ "jsonc-parser": "3.0.0",
+ "npm-package-arg": "9.0.2",
+ "npm-pick-manifest": "7.0.1",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "pacote": "13.3.0",
+ "resolve": "1.22.0",
+ "semver": "7.3.7",
+ "symbol-observable": "4.0.0",
+ "uuid": "8.3.2",
+ "yargs": "17.4.1"
+ },
+ "dependencies": {
+ "@angular-devkit/architect": {
+ "version": "0.1400.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1400.7.tgz",
+ "integrity": "sha512-8dv/Ql86dHajsHYjjr5jvpiV7uXWbt7Mz4K/rGiOi+zzDNKPcZcuCejulWhOySDcCPjT/an47Qcwr+awL4Wr4g==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "14.0.7",
+ "rxjs": "6.6.7"
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz",
+ "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==",
+ "dev": true,
+ "requires": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.0.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "jsonc-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/common": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.12.tgz",
+ "integrity": "sha512-oZunh9wfInFWhNO1P8uoEs/o4u8kerKMhw8GruywKm1TV7gHDP2Fi5WHGjFqq3XYptgBTPCTSEfyLX6Cwq1PUw==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/compiler": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.12.tgz",
+ "integrity": "sha512-u2MH9+NRwbbFDRNiPWPexed9CnCq9+pGHLuyACSP2uR6Ik68cE6cayeZbIeoEV5vWpda/XsLmJgPJysw7dAZLQ==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/compiler-cli": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.12.tgz",
+ "integrity": "sha512-9Gkb9KFkaQPz8XaS8ZwwTioRZ4ywykdAWyceICEi78/Y9ConYrTX2SbFogzI2dPUZU8a04tMlbqTSmHjVbJftQ==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.17.2",
+ "chokidar": "^3.0.0",
+ "convert-source-map": "^1.5.1",
+ "dependency-graph": "^0.11.0",
+ "magic-string": "^0.26.0",
+ "reflect-metadata": "^0.1.2",
+ "semver": "^7.0.0",
+ "sourcemap-codec": "^1.4.8",
+ "tslib": "^2.3.0",
+ "yargs": "^17.2.1"
+ }
+ },
+ "@angular/core": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.12.tgz",
+ "integrity": "sha512-sGQxU5u4uawwvJa6jOTmGoisJiQ5HIN/RoBw99CmoqZIVyUSg9IRJJC1KVdH8gbpWBNLkElZv21lwJTL/msWyg==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/elements": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-14.2.12.tgz",
+ "integrity": "sha512-FSAM5wz/Vi0DZZ9do4tuo1m8XyioBH9htsv1E2vStr6NXaq13U6TImQs9RcLIAOoKFV+pSuxSqHpT/Wfs9G30Q==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/forms": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.12.tgz",
+ "integrity": "sha512-7abYlGIT2JnAtutQUlH3fQS6QEpbfftgvsVcZJCyvX0rXL3u2w2vUQkDHJH4YJJp3AHFVCH4/l7R4VcaPnrwvA==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/material": {
+ "version": "14.2.7",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz",
+ "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/platform-browser": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.12.tgz",
+ "integrity": "sha512-vOarWym8ucl1gjYWCzdwyBha+MTvL381mvTTUu8aUx6nVhHFjv4bvpjlZnZgojecqUPyxOwmPLLHvCZPJVHZYg==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/platform-browser-dynamic": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.12.tgz",
+ "integrity": "sha512-oZhNJeaBmgw8+KBSYpKz2RYqEDyETC+HJXH8dwIFcP6BqqwL2NE70FdSR7EnOa5c41MEtTmMCGhrJSFR60x5/w==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@angular/router": {
+ "version": "14.2.12",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.12.tgz",
+ "integrity": "sha512-r5tVus5RJDNc4U2v0jMtjPiAS1xDsVsJ70lS313DgZmBDHIVZP1cWIehdxwgNlGwQQtAA36eG7toBwqUU3gb/A==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@assemblyscript/loader": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
+ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==",
+ "dev": true
+ },
+ "@auth0/auth0-angular": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-angular/-/auth0-angular-1.11.1.tgz",
+ "integrity": "sha512-q02tvjbela8TpJEaHbaw1vNkORLTOcjr/mRnxzE1rvIUbSfUGKueR8BTcT+a9mX0KIfF3lArGSF0p01bDO/P3w==",
+ "requires": {
+ "@auth0/auth0-spa-js": "^1.22.0",
+ "tslib": "^2.0.0"
+ }
+ },
+ "@auth0/auth0-spa-js": {
+ "version": "1.22.5",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.5.tgz",
+ "integrity": "sha512-6gaQcd+Eb8ZBcdQkrrm9undM7dY/rPvVdQN8s7rxxrviUCs7OopEygsfSkHf67IP4HtlCiE8dSW5/AipRUOw/A==",
+ "requires": {
+ "abortcontroller-polyfill": "^1.7.3",
+ "browser-tabs-lock": "^1.2.15",
+ "core-js": "^3.25.1",
+ "es-cookie": "~1.3.2",
+ "fast-text-encoding": "^1.0.6",
+ "promise-polyfill": "^8.2.3",
+ "unfetch": "^4.2.0"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+ "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.18.6"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.20.10",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz",
+ "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz",
+ "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.18.10",
+ "@babel/helper-compilation-targets": "^7.18.9",
+ "@babel/helper-module-transforms": "^7.18.9",
+ "@babel/helpers": "^7.18.9",
+ "@babel/parser": "^7.18.10",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.18.10",
+ "@babel/types": "^7.18.10",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.1",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.18.12",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz",
+ "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.10",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ },
+ "dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ }
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+ "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz",
+ "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.18.6",
+ "@babel/types": "^7.18.9"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
+ "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.20.5",
+ "@babel/helper-validator-option": "^7.18.6",
+ "browserslist": "^4.21.3",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.7.tgz",
+ "integrity": "sha512-LtoWbDXOaidEf50hmdDqn9g8VEzsorMexoWMQdQODbvmqYmaF23pBP5VNPAGIFHsFQCIeKokDiz3CH5Y2jlY6w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-member-expression-to-functions": "^7.20.7",
+ "@babel/helper-optimise-call-expression": "^7.18.6",
+ "@babel/helper-replace-supers": "^7.20.7",
+ "@babel/helper-split-export-declaration": "^7.18.6"
+ }
+ },
+ "@babel/helper-create-regexp-features-plugin": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz",
+ "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "regexpu-core": "^5.2.1"
+ }
+ },
+ "@babel/helper-define-polyfill-provider": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
+ "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.17.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2",
+ "semver": "^6.1.2"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz",
+ "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
+ "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.18.10",
+ "@babel/types": "^7.19.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz",
+ "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+ "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz",
+ "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "dependencies": {
+ "@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ }
+ }
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
+ "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "dev": true
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
+ "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-wrap-function": "^7.18.9",
+ "@babel/types": "^7.18.9"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz",
+ "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-member-expression-to-functions": "^7.20.7",
+ "@babel/helper-optimise-call-expression": "^7.18.6",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "dependencies": {
+ "@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ }
+ }
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.2"
+ }
+ },
+ "@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
+ "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.0"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
+ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
+ "dev": true
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz",
+ "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz",
+ "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "dependencies": {
+ "@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ }
+ }
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz",
+ "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==",
+ "dev": true
+ },
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
+ "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz",
+ "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.20.7"
+ }
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz",
+ "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/helper-remap-async-to-generator": "^7.18.9",
+ "@babel/plugin-syntax-async-generators": "^7.8.4"
+ }
+ },
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
+ "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-proposal-class-static-block": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz",
+ "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5"
+ }
+ },
+ "@babel/plugin-proposal-dynamic-import": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
+ "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
+ "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
+ "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-json-strings": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-logical-assignment-operators": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz",
+ "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
+ "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-numeric-separator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
+ "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
+ "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.20.7"
+ }
+ },
+ "@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
+ "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-optional-chaining": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz",
+ "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-private-methods": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+ "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz",
+ "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.20.5",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
+ "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ }
+ },
+ "@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ }
+ },
+ "@babel/plugin-syntax-dynamic-import": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+ "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-syntax-import-assertions": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz",
+ "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.19.0"
+ }
+ },
+ "@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ }
+ },
+ "@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz",
+ "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz",
+ "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/helper-remap-async-to-generator": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
+ "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.20.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.9.tgz",
+ "integrity": "sha512-hwZN0kr16UkIF/kR9F9x8gd1kTkQl1vyAF2lkUmlTuCtTKOGLE5blQctuxEeKXwz0dkArQ9RYL8+HLb/75KGMA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz",
+ "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-compilation-targets": "^7.20.7",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-optimise-call-expression": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-replace-supers": "^7.20.7",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz",
+ "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/template": "^7.20.7"
+ },
+ "dependencies": {
+ "@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ }
+ }
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz",
+ "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
+ "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz",
+ "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
+ "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.18.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz",
+ "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz",
+ "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.18.9",
+ "@babel/helper-function-name": "^7.18.9",
+ "@babel/helper-plugin-utils": "^7.18.9"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz",
+ "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ }
+ },
+ "@babel/plugin-transform-member-expression-literals": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
+ "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.7.tgz",
+ "integrity": "sha512-+1IVLD+dHOzRZWNFFSoyPZz4ffsVmOP+OhhjeahLKpU97v/52LcCb9RabRl5eHM1/HAuH5Dl0q9Pyzrq1v2otQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.7.tgz",
+ "integrity": "sha512-76jqqFiFdCD+RJwEdtBHUG2/rEKQAmpejPbAKyQECEE3/y4U5CMPc9IXvipS990vgQhzq+ZRw6WJ+q4xJ/P24w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-simple-access": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.19.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz",
+ "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-module-transforms": "^7.19.6",
+ "@babel/helper-plugin-utils": "^7.19.0",
+ "@babel/helper-validator-identifier": "^7.19.1"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
+ "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz",
+ "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.20.5",
+ "@babel/helper-plugin-utils": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz",
+ "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
+ "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6",
+ "@babel/helper-replace-supers": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz",
+ "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ }
+ },
+ "@babel/plugin-transform-property-literals": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
+ "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz",
+ "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "regenerator-transform": "^0.15.1"
+ }
+ },
+ "@babel/plugin-transform-reserved-words": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
+ "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-runtime": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz",
+ "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "babel-plugin-polyfill-corejs2": "^0.3.2",
+ "babel-plugin-polyfill-corejs3": "^0.5.3",
+ "babel-plugin-polyfill-regenerator": "^0.4.0",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
+ "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz",
+ "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
+ "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz",
+ "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz",
+ "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ }
+ },
+ "@babel/plugin-transform-unicode-escapes": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz",
+ "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.9"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
+ "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz",
+ "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.18.8",
+ "@babel/helper-compilation-targets": "^7.18.9",
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/helper-validator-option": "^7.18.6",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9",
+ "@babel/plugin-proposal-async-generator-functions": "^7.18.10",
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@babel/plugin-proposal-class-static-block": "^7.18.6",
+ "@babel/plugin-proposal-dynamic-import": "^7.18.6",
+ "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+ "@babel/plugin-proposal-json-strings": "^7.18.6",
+ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+ "@babel/plugin-proposal-numeric-separator": "^7.18.6",
+ "@babel/plugin-proposal-object-rest-spread": "^7.18.9",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
+ "@babel/plugin-proposal-optional-chaining": "^7.18.9",
+ "@babel/plugin-proposal-private-methods": "^7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+ "@babel/plugin-syntax-import-assertions": "^7.18.6",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5",
+ "@babel/plugin-transform-arrow-functions": "^7.18.6",
+ "@babel/plugin-transform-async-to-generator": "^7.18.6",
+ "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
+ "@babel/plugin-transform-block-scoping": "^7.18.9",
+ "@babel/plugin-transform-classes": "^7.18.9",
+ "@babel/plugin-transform-computed-properties": "^7.18.9",
+ "@babel/plugin-transform-destructuring": "^7.18.9",
+ "@babel/plugin-transform-dotall-regex": "^7.18.6",
+ "@babel/plugin-transform-duplicate-keys": "^7.18.9",
+ "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
+ "@babel/plugin-transform-for-of": "^7.18.8",
+ "@babel/plugin-transform-function-name": "^7.18.9",
+ "@babel/plugin-transform-literals": "^7.18.9",
+ "@babel/plugin-transform-member-expression-literals": "^7.18.6",
+ "@babel/plugin-transform-modules-amd": "^7.18.6",
+ "@babel/plugin-transform-modules-commonjs": "^7.18.6",
+ "@babel/plugin-transform-modules-systemjs": "^7.18.9",
+ "@babel/plugin-transform-modules-umd": "^7.18.6",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6",
+ "@babel/plugin-transform-new-target": "^7.18.6",
+ "@babel/plugin-transform-object-super": "^7.18.6",
+ "@babel/plugin-transform-parameters": "^7.18.8",
+ "@babel/plugin-transform-property-literals": "^7.18.6",
+ "@babel/plugin-transform-regenerator": "^7.18.6",
+ "@babel/plugin-transform-reserved-words": "^7.18.6",
+ "@babel/plugin-transform-shorthand-properties": "^7.18.6",
+ "@babel/plugin-transform-spread": "^7.18.9",
+ "@babel/plugin-transform-sticky-regex": "^7.18.6",
+ "@babel/plugin-transform-template-literals": "^7.18.9",
+ "@babel/plugin-transform-typeof-symbol": "^7.18.9",
+ "@babel/plugin-transform-unicode-escapes": "^7.18.10",
+ "@babel/plugin-transform-unicode-regex": "^7.18.6",
+ "@babel/preset-modules": "^0.1.5",
+ "@babel/types": "^7.18.10",
+ "babel-plugin-polyfill-corejs2": "^0.3.2",
+ "babel-plugin-polyfill-corejs3": "^0.5.3",
+ "babel-plugin-polyfill-regenerator": "^0.4.0",
+ "core-js-compat": "^3.22.1",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/preset-modules": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
+ "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+ "@babel/plugin-transform-dotall-regex": "^7.4.4",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
+ "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
+ "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.18.10",
+ "@babel/types": "^7.18.10"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.20.10",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.10.tgz",
+ "integrity": "sha512-oSf1juCgymrSez8NI4A2sr4+uB/mFd9MXplYGPEBnfAuWmmyeVcHa6xLPiaRBcXkcb/28bgxmQLTVwFKE1yfsg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.7",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz",
+ "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.7",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
+ "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true
+ },
+ "@csstools/postcss-cascade-layers": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz",
+ "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==",
+ "dev": true,
+ "requires": {
+ "@csstools/selector-specificity": "^2.0.2",
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "@csstools/postcss-color-function": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz",
+ "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==",
+ "dev": true,
+ "requires": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-font-format-keywords": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz",
+ "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-hwb-function": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz",
+ "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-ic-unit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz",
+ "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==",
+ "dev": true,
+ "requires": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-is-pseudo-class": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz",
+ "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==",
+ "dev": true,
+ "requires": {
+ "@csstools/selector-specificity": "^2.0.0",
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "@csstools/postcss-nested-calc": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz",
+ "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-normalize-display-values": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz",
+ "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-oklab-function": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz",
+ "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==",
+ "dev": true,
+ "requires": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-progressive-custom-properties": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz",
+ "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-stepped-value-functions": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz",
+ "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-text-decoration-shorthand": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz",
+ "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-trigonometric-functions": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz",
+ "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "@csstools/postcss-unset-value": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz",
+ "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==",
+ "dev": true,
+ "requires": {}
+ },
+ "@csstools/selector-specificity": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz",
+ "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==",
+ "dev": true,
+ "requires": {}
+ },
+ "@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz",
+ "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==",
+ "dev": true,
+ "optional": true
+ },
+ "@eslint/eslintrc": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz",
+ "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.4.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "globals": {
+ "version": "13.19.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
+ "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ }
+ }
+ },
+ "@gar/promisify": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
+ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
+ "dev": true
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+ "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ }
+ },
+ "@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+ "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ }
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "@leichtgewicht/ip-codec": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
+ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
+ "dev": true
+ },
+ "@ngrx/effects": {
+ "version": "14.3.2",
+ "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-14.3.2.tgz",
+ "integrity": "sha512-6bpGfA44jzwhBcmNaTwVgnFmYOX9iKPFpXyetDe41tVESo1CsNhUBPTmISDXKN9Mx2mwGbsMxrn6QFRypSsKAQ==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@ngrx/store": {
+ "version": "14.3.2",
+ "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-14.3.2.tgz",
+ "integrity": "sha512-XGHjr0arh6gClo8Ce+xqJLvW9PkeXPW2tCo9Z5qMtHFI/z5dUppLVKGmMgD/fQBDyoqWTK5xu+89tDkdeZfRjQ==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@ngtools/webpack": {
+ "version": "14.2.10",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.10.tgz",
+ "integrity": "sha512-sLHapZLVub6mEz5b19tf1VfIV1w3tYfg7FNPLeni79aldxu1FbP1v2WmiFAnMzrswqyK0bhTtxrl+Z/CLKqyoQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "@ngxs/devtools-plugin": {
+ "version": "3.7.6",
+ "resolved": "https://registry.npmjs.org/@ngxs/devtools-plugin/-/devtools-plugin-3.7.6.tgz",
+ "integrity": "sha512-KbFak7ZF8U23z1TDwpkKqReNMco4c958SAJ02JOc89U+O8scpwOCTUh/aJxoy8ZsqV4/F1eb4DeZ5EtXXXznwg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@ngxs/store": {
+ "version": "3.7.6",
+ "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.6.tgz",
+ "integrity": "sha512-ljnrkVrO/HmDakQImWr9G2NbpbNd8MjoPI3Ad2GsIbK/Ey4psEyMGgg6jcaMyhDfDAs2Z69Q+5TVJS0GV4euMw==",
+ "requires": {
+ "tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@npmcli/fs": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
+ "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
+ "dev": true,
+ "requires": {
+ "@gar/promisify": "^1.1.3",
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/git": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz",
+ "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==",
+ "dev": true,
+ "requires": {
+ "@npmcli/promise-spawn": "^3.0.0",
+ "lru-cache": "^7.4.4",
+ "mkdirp": "^1.0.4",
+ "npm-pick-manifest": "^7.0.0",
+ "proc-log": "^2.0.0",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^2.0.2"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true
+ }
+ }
+ },
+ "@npmcli/installed-package-contents": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz",
+ "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==",
+ "dev": true,
+ "requires": {
+ "npm-bundled": "^1.1.1",
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "@npmcli/move-file": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
+ "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "@npmcli/node-gyp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz",
+ "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==",
+ "dev": true
+ },
+ "@npmcli/promise-spawn": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz",
+ "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==",
+ "dev": true,
+ "requires": {
+ "infer-owner": "^1.0.4"
+ }
+ },
+ "@npmcli/run-script": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-3.0.3.tgz",
+ "integrity": "sha512-ZXL6qgC5NjwfZJ2nET+ZSLEz/PJgJ/5CU90C2S66dZY4Jw73DasS4ZCXuy/KHWYP0imjJ4VtA+Gebb5BxxKp9Q==",
+ "dev": true,
+ "requires": {
+ "@npmcli/node-gyp": "^2.0.0",
+ "@npmcli/promise-spawn": "^3.0.0",
+ "node-gyp": "^8.4.1",
+ "read-package-json-fast": "^2.0.3"
+ }
+ },
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "@schematics/angular": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.0.7.tgz",
+ "integrity": "sha512-I0v1gNFpm9ReL/hUzwjjOa+hk0qvlXv/vjITAWnlUV5dba6FZxzwsrTGsGO6t5XMNsm6QtwpDYDRdy9uy/n/1g==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "14.0.7",
+ "@angular-devkit/schematics": "14.0.7",
+ "jsonc-parser": "3.0.0"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "14.0.7",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz",
+ "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==",
+ "dev": true,
+ "requires": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.0.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ }
+ },
+ "jsonc-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@socket.io/component-emitter": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
+ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
+ "dev": true
+ },
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true
+ },
+ "@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/bonjour": {
+ "version": "3.5.10",
+ "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz",
+ "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/chrome": {
+ "version": "0.0.204",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.204.tgz",
+ "integrity": "sha512-EvnHfxMHUWP5EAlRMK66uIEUiy36t72vg5RwmzQv9tdIl2ZmAp92NwvmEZJKpbRnIMTEc2BmSmtrFiEISUJ0Sw==",
+ "dev": true,
+ "requires": {
+ "@types/filesystem": "*",
+ "@types/har-format": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/connect-history-api-fallback": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz",
+ "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==",
+ "dev": true,
+ "requires": {
+ "@types/express-serve-static-core": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
+ "dev": true
+ },
+ "@types/cors": {
+ "version": "2.8.13",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
+ "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/dateformat": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.0.tgz",
+ "integrity": "sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==",
+ "dev": true
+ },
+ "@types/eslint": {
+ "version": "8.4.10",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
+ "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
+ "requires": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "@types/eslint-scope": {
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
+ "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
+ "requires": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "@types/estree": {
+ "version": "0.0.51",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
+ },
+ "@types/express": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz",
+ "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==",
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.31",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.31",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
+ "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/filesystem": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz",
+ "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==",
+ "dev": true,
+ "requires": {
+ "@types/filewriter": "*"
+ }
+ },
+ "@types/filewriter": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz",
+ "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
+ "dev": true
+ },
+ "@types/har-format": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
+ "integrity": "sha512-o0J30wqycjF5miWDKYKKzzOU1ZTLuA42HZ4HE7/zqTOc/jTLdQ5NhYWvsRQo45Nfi1KHoRdNhteSI4BAxTF1Pg==",
+ "dev": true
+ },
+ "@types/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
+ },
+ "@types/http-proxy": {
+ "version": "1.17.9",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
+ "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/jasmine": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.1.tgz",
+ "integrity": "sha512-Vu8l+UGcshYmV1VWwULgnV/2RDbBaO6i2Ptx7nd//oJPIZGhoI1YLST4VKagD2Pq/Bc2/7zvtvhM7F3p4SN7kQ==",
+ "dev": true
+ },
+ "@types/jquery": {
+ "version": "3.5.14",
+ "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
+ "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
+ "dev": true,
+ "requires": {
+ "@types/sizzle": "*"
+ }
+ },
+ "@types/jsbn": {
+ "version": "1.2.30",
+ "resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.30.tgz",
+ "integrity": "sha512-VZouplBofjq3YOIHLNRBDxILs/nAArdTZ2QP1ooflyhS+yPExWsFE+i2paBIBb7OI3NJShfcde/nogqk4SPB/Q==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
+ },
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "@types/jsonwebtoken": {
+ "version": "8.5.9",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz",
+ "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
+ },
+ "@types/mime": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
+ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
+ },
+ "@types/node": {
+ "version": "18.11.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz",
+ "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng=="
+ },
+ "@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+ "dev": true
+ },
+ "@types/q": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
+ "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==",
+ "dev": true
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+ },
+ "@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "dev": true
+ },
+ "@types/selenium-webdriver": {
+ "version": "3.0.20",
+ "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.20.tgz",
+ "integrity": "sha512-6d8Q5fqS9DWOXEhMDiF6/2FjyHdmP/jSTAUyeQR7QwrFeNmYyzmvGxD5aLIHL445HjWgibs0eAig+KPnbaesXA==",
+ "dev": true
+ },
+ "@types/semver": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+ "dev": true
+ },
+ "@types/serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
+ "@types/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "requires": {
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/sizzle": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
+ "dev": true
+ },
+ "@types/sockjs": {
+ "version": "0.3.33",
+ "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
+ "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/three": {
+ "version": "0.143.2",
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.143.2.tgz",
+ "integrity": "sha512-HjsRWvd6rsXViFeOdU97/pHNDQknzJbFI0/5MrQ0joOlK0uixQH40sDJs/LwkNHhFYUvVENXAUQdKDeiQChHDw==",
+ "dev": true,
+ "requires": {
+ "@types/webxr": "*"
+ }
+ },
+ "@types/w3c-web-usb": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz",
+ "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==",
+ "dev": true
+ },
+ "@types/webxr": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.0.tgz",
+ "integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==",
+ "dev": true
+ },
+ "@types/ws": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+ "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz",
+ "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "5.47.0",
+ "@typescript-eslint/type-utils": "5.47.0",
+ "@typescript-eslint/utils": "5.47.0",
+ "debug": "^4.3.4",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz",
+ "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "5.47.0",
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/typescript-estree": "5.47.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz",
+ "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/visitor-keys": "5.47.0"
+ }
+ },
+ "@typescript-eslint/type-utils": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz",
+ "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "5.47.0",
+ "@typescript-eslint/utils": "5.47.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
+ "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz",
+ "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/visitor-keys": "5.47.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ }
+ },
+ "@typescript-eslint/utils": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz",
+ "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.47.0",
+ "@typescript-eslint/types": "5.47.0",
+ "@typescript-eslint/typescript-estree": "5.47.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0",
+ "semver": "^7.3.7"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "5.47.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
+ "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "5.47.0",
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+ "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+ "requires": {
+ "@webassemblyjs/helper-numbers": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+ "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+ "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+ "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
+ },
+ "@webassemblyjs/helper-numbers": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+ "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+ "requires": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+ "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+ "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+ "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+ "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+ "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+ "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/helper-wasm-section": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-opt": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "@webassemblyjs/wast-printer": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+ "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+ "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+ "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+ "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webpack-cli/configtest": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
+ "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
+ "requires": {}
+ },
+ "@webpack-cli/info": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
+ "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
+ "requires": {
+ "envinfo": "^7.7.3"
+ }
+ },
+ "@webpack-cli/serve": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
+ "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
+ "requires": {}
+ },
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
+ },
+ "@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
+ },
+ "@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "dev": true
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "abortcontroller-polyfill": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
+ "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ=="
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA=="
+ },
+ "acorn-import-assertions": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+ "requires": {}
+ },
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "adjust-sourcemap-loader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
+ "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "regex-parser": "^2.2.11"
+ }
+ },
+ "adm-zip": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "agentkeepalive": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
+ "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "depd": "^1.1.2",
+ "humanize-ms": "^1.2.1"
+ },
+ "dependencies": {
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true
+ }
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.0"
+ }
+ },
+ "ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
+ "angular2-template-loader": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz",
+ "integrity": "sha512-jBSrm2yDsTA48GG0H57upn8rmTfJS3/R7EhUeAL/3ryWS8deT9You8UQKWpW4eVSEY7uMJ52iFwpOYXc8QEtGg==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^0.2.15"
+ },
+ "dependencies": {
+ "big.js": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
+ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+ "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==",
+ "dev": true,
+ "requires": {
+ "big.js": "^3.1.3",
+ "emojis-list": "^2.0.0",
+ "json5": "^0.5.0",
+ "object-assign": "^4.0.1"
+ }
+ }
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.21.3"
+ }
+ },
+ "ansi-html-community": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+ "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "app-module-path": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz",
+ "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==",
+ "dev": true
+ },
+ "aproba": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+ "dev": true
+ },
+ "are-we-there-yet": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+ "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+ "dev": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-flatten": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true
+ },
+ "ast-module-types": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz",
+ "integrity": "sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "auth0": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/auth0/-/auth0-3.0.1.tgz",
+ "integrity": "sha512-3VZd+irYBIo16rEtYUnzK8FoOCgShiYsv1foQA5Zzcsj44RLq5mSVhyuYTIg9/h0Al2CXWalxYK/lrMtkQftzw==",
+ "requires": {
+ "axios": "^0.27.2",
+ "form-data": "^3.0.1",
+ "jsonwebtoken": "^9.0.0",
+ "jwks-rsa": "^3.0.0",
+ "lru-memoizer": "^2.1.4",
+ "rest-facade": "^1.16.3",
+ "retry": "^0.13.1"
+ }
+ },
+ "autoprefixer": {
+ "version": "10.4.13",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
+ "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "caniuse-lite": "^1.0.30001426",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true
+ },
+ "axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "requires": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ }
+ }
+ },
+ "babel-loader": {
+ "version": "8.2.5",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz",
+ "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==",
+ "dev": true,
+ "requires": {
+ "find-cache-dir": "^3.3.1",
+ "loader-utils": "^2.0.0",
+ "make-dir": "^3.1.0",
+ "schema-utils": "^2.6.5"
+ }
+ },
+ "babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ }
+ },
+ "babel-plugin-polyfill-corejs2": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
+ "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.17.7",
+ "@babel/helper-define-polyfill-provider": "^0.3.3",
+ "semver": "^6.1.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "babel-plugin-polyfill-corejs3": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz",
+ "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-define-polyfill-provider": "^0.3.2",
+ "core-js-compat": "^3.21.0"
+ }
+ },
+ "babel-plugin-polyfill-regenerator": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz",
+ "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-define-polyfill-provider": "^0.3.3"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base64-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true
+ },
+ "base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "blocking-proxy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
+ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "body-parser": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "bonjour-service": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz",
+ "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==",
+ "dev": true,
+ "requires": {
+ "array-flatten": "^2.1.2",
+ "dns-equal": "^1.0.0",
+ "fast-deep-equal": "^3.1.3",
+ "multicast-dns": "^7.2.5"
+ }
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-tabs-lock": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.2.15.tgz",
+ "integrity": "sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==",
+ "requires": {
+ "lodash": ">=4.17.21"
+ }
+ },
+ "browserslist": {
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
+ "requires": {
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
+ "node-releases": "^2.0.6",
+ "update-browserslist-db": "^1.0.9"
+ }
+ },
+ "browserstack": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz",
+ "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ }
+ }
+ }
+ },
+ "buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
+ "buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
+ "dev": true
+ },
+ "builtins": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
+ "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.0.0"
+ }
+ },
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true
+ },
+ "cacache": {
+ "version": "16.1.2",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz",
+ "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==",
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^2.1.0",
+ "@npmcli/move-file": "^2.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "glob": "^8.0.1",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "mkdirp": "^1.0.4",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^1.1.1"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true
+ }
+ }
+ },
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "requires": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001441",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz",
+ "integrity": "sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg=="
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "change-case": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.3.1.tgz",
+ "integrity": "sha512-3HE5jrTqqn9jeKzD0+yWi7FU4OMicLbwB57ph4bpwEn5jGi3hZug5WjZjnBD2RY7YyTKAAck86ACfShXUWJKLg==",
+ "requires": {
+ "camel-case": "^1.1.1",
+ "constant-case": "^1.1.0",
+ "dot-case": "^1.1.0",
+ "is-lower-case": "^1.1.0",
+ "is-upper-case": "^1.1.0",
+ "lower-case": "^1.1.1",
+ "lower-case-first": "^1.0.0",
+ "param-case": "^1.1.0",
+ "pascal-case": "^1.1.0",
+ "path-case": "^1.1.0",
+ "sentence-case": "^1.1.1",
+ "snake-case": "^1.1.0",
+ "swap-case": "^1.1.0",
+ "title-case": "^1.1.0",
+ "upper-case": "^1.1.1",
+ "upper-case-first": "^1.1.0"
+ },
+ "dependencies": {
+ "camel-case": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz",
+ "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==",
+ "requires": {
+ "sentence-case": "^1.1.1",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "dot-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz",
+ "integrity": "sha512-NzEIt12UjECXi6JZ/R/nBey6EE1qCN0yUTEFaPIaKW0AcOEwlKqujtcJVbtSfLNnj3CDoXLQyli79vAaqohyvw==",
+ "requires": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ },
+ "param-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz",
+ "integrity": "sha512-gksk6zeZQxwBm1AHsKh+XDFsTGf1LvdZSkkpSIkfDtzW+EQj/P2PBgNb3Cs0Y9Xxqmbciv2JZe3fWU6Xbher+Q==",
+ "requires": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "pascal-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz",
+ "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==",
+ "requires": {
+ "camel-case": "^1.1.1",
+ "upper-case-first": "^1.1.0"
+ }
+ }
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true
+ },
+ "chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
+ },
+ "clean-css": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
+ "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==",
+ "requires": {
+ "source-map": "~0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "dev": true
+ },
+ "cli-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true
+ },
+ "clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "requires": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true
+ },
+ "colorette": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
+ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
+ },
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "connect-history-api-fallback": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
+ "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+ "dev": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+ "dev": true
+ },
+ "constant-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz",
+ "integrity": "sha512-FQ/HuOuSnX6nIF8OnofRWj+KnOpGAHXQpOKHmsL1sAnuLwu6r5mHGK+mJc0SkHkbmNfcU/SauqXLTEOL1JQfJA==",
+ "requires": {
+ "snake-case": "^1.1.0",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.2.1"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
+ },
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+ "dev": true
+ },
+ "cookiejar": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
+ "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ=="
+ },
+ "copy-anything": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+ "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+ "dev": true,
+ "requires": {
+ "is-what": "^3.14.1"
+ }
+ },
+ "copy-webpack-plugin": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz",
+ "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==",
+ "dev": true,
+ "requires": {
+ "fast-glob": "^3.2.11",
+ "glob-parent": "^6.0.1",
+ "globby": "^13.1.1",
+ "normalize-path": "^3.0.0",
+ "schema-utils": "^4.0.0",
+ "serialize-javascript": "^6.0.0"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "globby": {
+ "version": "13.1.3",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz",
+ "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==",
+ "dev": true,
+ "requires": {
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.11",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^4.0.0"
+ }
+ },
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ },
+ "slash": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
+ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
+ "dev": true
+ }
+ }
+ },
+ "core-js": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz",
+ "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA=="
+ },
+ "core-js-compat": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz",
+ "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4"
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dev": true,
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ }
+ },
+ "critters": {
+ "version": "0.0.16",
+ "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz",
+ "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "css-select": "^4.2.0",
+ "parse5": "^6.0.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.1",
+ "postcss": "^8.3.7",
+ "pretty-bytes": "^5.3.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "css-blank-pseudo": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz",
+ "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.9"
+ }
+ },
+ "css-has-pseudo": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz",
+ "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.9"
+ }
+ },
+ "css-line-break": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+ "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+ "requires": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "css-loader": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
+ "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.7",
+ "postcss-modules-extract-imports": "^3.0.0",
+ "postcss-modules-local-by-default": "^4.0.0",
+ "postcss-modules-scope": "^3.0.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "css-prefers-color-scheme": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz",
+ "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==",
+ "dev": true,
+ "requires": {}
+ },
+ "css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
+ },
+ "cssdb": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz",
+ "integrity": "sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg==",
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
+ "dev": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "date-format": {
+ "version": "4.0.14",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
+ "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
+ "dev": true
+ },
+ "dateformat": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz",
+ "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA=="
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true
+ },
+ "deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "deepmerge": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
+ "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="
+ },
+ "default-gateway": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
+ "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
+ "dev": true,
+ "requires": {
+ "execa": "^5.0.0"
+ }
+ },
+ "defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "requires": {
+ "clone": "^1.0.2"
+ }
+ },
+ "define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "dependencies": {
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "dev": true
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true
+ },
+ "dependency-graph": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+ "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+ "dev": true
+ },
+ "dependency-tree": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-8.1.2.tgz",
+ "integrity": "sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.3",
+ "debug": "^4.3.1",
+ "filing-cabinet": "^3.0.1",
+ "precinct": "^8.0.0",
+ "typescript": "^3.9.7"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true
+ }
+ }
+ },
+ "destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true
+ },
+ "detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "dev": true
+ },
+ "detective-amd": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-3.1.2.tgz",
+ "integrity": "sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^3.0.0",
+ "escodegen": "^2.0.0",
+ "get-amd-module-type": "^3.0.0",
+ "node-source-walk": "^4.2.0"
+ }
+ },
+ "detective-cjs": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-3.1.3.tgz",
+ "integrity": "sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^3.0.0",
+ "node-source-walk": "^4.0.0"
+ }
+ },
+ "detective-es6": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz",
+ "integrity": "sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw==",
+ "dev": true,
+ "requires": {
+ "node-source-walk": "^4.0.0"
+ }
+ },
+ "detective-less": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/detective-less/-/detective-less-1.0.2.tgz",
+ "integrity": "sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.0.0",
+ "gonzales-pe": "^4.2.3",
+ "node-source-walk": "^4.0.0"
+ }
+ },
+ "detective-postcss": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-5.1.3.tgz",
+ "integrity": "sha512-Wo7PUpF6wqeT1aRgajdyIdDRjFFJVxlXPRAlT1aankH/RVOgrJuEZFZ4ABxYXdzaRPO5Lkg8rHxsxpLnxdJIYA==",
+ "dev": true,
+ "requires": {
+ "is-url": "^1.2.4",
+ "postcss": "^8.4.6",
+ "postcss-values-parser": "^5.0.0"
+ }
+ },
+ "detective-sass": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-3.0.2.tgz",
+ "integrity": "sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g==",
+ "dev": true,
+ "requires": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^4.0.0"
+ }
+ },
+ "detective-scss": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-2.0.2.tgz",
+ "integrity": "sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg==",
+ "dev": true,
+ "requires": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^4.0.0"
+ }
+ },
+ "detective-stylus": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-1.0.3.tgz",
+ "integrity": "sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q==",
+ "dev": true
+ },
+ "detective-typescript": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-7.0.2.tgz",
+ "integrity": "sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "^4.33.0",
+ "ast-module-types": "^2.7.1",
+ "node-source-walk": "^4.2.0",
+ "typescript": "^3.9.10"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz",
+ "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz",
+ "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.33.0",
+ "@typescript-eslint/visitor-keys": "4.33.0",
+ "debug": "^4.3.1",
+ "globby": "^11.0.3",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz",
+ "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.33.0",
+ "eslint-visitor-keys": "^2.0.0"
+ }
+ },
+ "ast-module-types": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz",
+ "integrity": "sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw==",
+ "dev": true
+ },
+ "eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true
+ }
+ }
+ },
+ "di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
+ "dev": true
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "dns-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+ "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
+ "dev": true
+ },
+ "dns-packet": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz",
+ "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==",
+ "dev": true,
+ "requires": {
+ "@leichtgewicht/ip-codec": "^2.0.1"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-converter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+ "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "requires": {
+ "utila": "~0.4"
+ }
+ },
+ "dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==",
+ "dev": true,
+ "requires": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+ },
+ "domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "requires": {
+ "domelementtype": "^2.2.0"
+ }
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "requires": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ }
+ },
+ "dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "requires": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ },
+ "dependencies": {
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ }
+ }
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.4.284",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
+ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "iconv-lite": "^0.6.2"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
+ }
+ },
+ "engine.io": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
+ "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
+ "dev": true,
+ "requires": {
+ "@types/cookie": "^0.4.1",
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.0.3",
+ "ws": "~8.2.3"
+ }
+ },
+ "engine.io-parser": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
+ "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
+ "dev": true
+ },
+ "enhanced-resolve": {
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
+ "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==",
+ "dev": true
+ },
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true
+ },
+ "envinfo": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
+ },
+ "err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "dev": true
+ },
+ "errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-cookie": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz",
+ "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q=="
+ },
+ "es-module-lexer": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
+ },
+ "es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "esbuild": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz",
+ "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@esbuild/linux-loong64": "0.15.5",
+ "esbuild-android-64": "0.15.5",
+ "esbuild-android-arm64": "0.15.5",
+ "esbuild-darwin-64": "0.15.5",
+ "esbuild-darwin-arm64": "0.15.5",
+ "esbuild-freebsd-64": "0.15.5",
+ "esbuild-freebsd-arm64": "0.15.5",
+ "esbuild-linux-32": "0.15.5",
+ "esbuild-linux-64": "0.15.5",
+ "esbuild-linux-arm": "0.15.5",
+ "esbuild-linux-arm64": "0.15.5",
+ "esbuild-linux-mips64le": "0.15.5",
+ "esbuild-linux-ppc64le": "0.15.5",
+ "esbuild-linux-riscv64": "0.15.5",
+ "esbuild-linux-s390x": "0.15.5",
+ "esbuild-netbsd-64": "0.15.5",
+ "esbuild-openbsd-64": "0.15.5",
+ "esbuild-sunos-64": "0.15.5",
+ "esbuild-windows-32": "0.15.5",
+ "esbuild-windows-64": "0.15.5",
+ "esbuild-windows-arm64": "0.15.5"
+ }
+ },
+ "esbuild-android-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz",
+ "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-android-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz",
+ "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-darwin-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz",
+ "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-darwin-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz",
+ "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-freebsd-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz",
+ "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-freebsd-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz",
+ "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-32": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz",
+ "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz",
+ "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-arm": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz",
+ "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz",
+ "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-mips64le": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz",
+ "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-ppc64le": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz",
+ "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-riscv64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz",
+ "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-s390x": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz",
+ "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-netbsd-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz",
+ "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-openbsd-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz",
+ "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-sunos-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz",
+ "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-wasm": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz",
+ "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==",
+ "dev": true
+ },
+ "esbuild-windows-32": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz",
+ "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz",
+ "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-arm64": {
+ "version": "0.15.5",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz",
+ "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==",
+ "dev": true,
+ "optional": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+ },
+ "escodegen": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ }
+ }
+ },
+ "eslint": {
+ "version": "8.30.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz",
+ "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==",
+ "dev": true,
+ "requires": {
+ "@eslint/eslintrc": "^1.4.0",
+ "@humanwhocodes/config-array": "^0.11.8",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.1.1",
+ "eslint-utils": "^3.0.0",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.4.0",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "grapheme-splitter": "^1.0.4",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-sdsl": "^4.1.4",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "regexpp": "^3.2.0",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "eslint-scope": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+ "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "globals": {
+ "version": "13.19.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
+ "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-plugin-prettier": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+ "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0"
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+ "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+ "dev": true
+ },
+ "espree": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+ "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.8.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ }
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true
+ },
+ "eventemitter-asyncresource": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz",
+ "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ },
+ "events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
+ },
+ "execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true
+ },
+ "express": {
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.1",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.5.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.11.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "dev": true
+ },
+ "cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "finalhandler": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
+ },
+ "fast-text-encoding": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
+ "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
+ },
+ "fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="
+ },
+ "fastq": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
+ "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "filing-cabinet": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.3.0.tgz",
+ "integrity": "sha512-Tnbpbme1ONaHXV5DGcw0OFpcfP3p2itRf5VXO1bguBXdIewDbK6ZFBK//DGKM0BuCzaQLQNY4f5gljzxY1VCUw==",
+ "dev": true,
+ "requires": {
+ "app-module-path": "^2.2.0",
+ "commander": "^2.20.3",
+ "debug": "^4.3.3",
+ "enhanced-resolve": "^5.8.3",
+ "is-relative-path": "^1.0.2",
+ "module-definition": "^3.3.1",
+ "module-lookup-amd": "^7.0.1",
+ "resolve": "^1.21.0",
+ "resolve-dependency-path": "^2.0.0",
+ "sass-lookup": "^3.0.0",
+ "stylus-lookup": "^3.0.1",
+ "tsconfig-paths": "^3.10.1",
+ "typescript": "^3.9.7"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true
+ }
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ }
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "flatten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
+ "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true
+ },
+ "form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "formidable": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
+ "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ=="
+ },
+ "forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true
+ },
+ "fraction.js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
+ "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+ "dev": true
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "fs-monkey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+ "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "gauge": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+ "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^3.0.7",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ }
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true
+ },
+ "get-amd-module-type": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz",
+ "integrity": "sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^3.0.0",
+ "node-source-walk": "^4.2.2"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "get-own-enumerable-property-symbols": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
+ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
+ "dev": true
+ },
+ "get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "gl-matrix": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
+ "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
+ },
+ "glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ }
+ },
+ "gonzales-pe": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
+ "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ },
+ "grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "graphviz": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.9.tgz",
+ "integrity": "sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg==",
+ "dev": true,
+ "requires": {
+ "temp": "~0.4.0"
+ }
+ },
+ "handle-thing": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+ "dev": true
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ }
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ }
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+ "dev": true
+ },
+ "hdr-histogram-js": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz",
+ "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==",
+ "dev": true,
+ "requires": {
+ "@assemblyscript/loader": "^0.10.1",
+ "base64-js": "^1.2.0",
+ "pako": "^1.0.3"
+ }
+ },
+ "hdr-histogram-percentiles-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz",
+ "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==",
+ "dev": true
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
+ },
+ "hosted-git-info": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz",
+ "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^7.5.1"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true
+ }
+ }
+ },
+ "hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "html-entities": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
+ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
+ "dev": true
+ },
+ "html-loader": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.1.2.tgz",
+ "integrity": "sha512-9WQlLiAV5N9fCna4MUmBW/ifaUbuFZ2r7IZmtXzhyfyi4zgPEjXsmsYCKs+yT873MzRj+f1WMjuAiPNA7C6Tcw==",
+ "requires": {
+ "html-minifier-terser": "^6.0.2",
+ "parse5": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ }
+ }
+ },
+ "html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+ "requires": {
+ "camel-case": "^4.1.2",
+ "clean-css": "^5.2.2",
+ "commander": "^8.3.0",
+ "he": "^1.2.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.10.0"
+ }
+ },
+ "html-webpack-inline-source-plugin": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-1.0.0-beta.2.tgz",
+ "integrity": "sha512-ydsEKdp0tnbmnqRAH2WSSMXerCNYhjes5b79uvP2BU3p6cyk+6ucNMsw5b5xD1QxphgvBBA3QqVmdcpu8QLlRQ==",
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "slash": "^1.0.0",
+ "source-map-url": "^0.4.0"
+ },
+ "dependencies": {
+ "slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg=="
+ }
+ }
+ },
+ "html-webpack-plugin": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+ "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+ "requires": {
+ "@types/html-minifier-terser": "^6.0.0",
+ "html-minifier-terser": "^6.0.2",
+ "lodash": "^4.17.21",
+ "pretty-error": "^4.0.0",
+ "tapable": "^2.0.0"
+ }
+ },
+ "html2canvas": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+ "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+ "requires": {
+ "css-line-break": "^2.1.0",
+ "text-segmentation": "^1.0.3"
+ }
+ },
+ "htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.0.0",
+ "domutils": "^2.5.2",
+ "entities": "^2.0.0"
+ }
+ },
+ "http-cache-semantics": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+ "dev": true
+ },
+ "http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "dependencies": {
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ }
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+ "dev": true
+ },
+ "http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "http-proxy-middleware": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
+ "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
+ "dev": true,
+ "requires": {
+ "@types/http-proxy": "^1.17.8",
+ "http-proxy": "^1.18.1",
+ "is-glob": "^4.0.1",
+ "is-plain-obj": "^3.0.0",
+ "micromatch": "^4.0.2"
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true
+ },
+ "humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
+ "requires": {}
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true
+ },
+ "ignore-walk": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz",
+ "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==",
+ "dev": true,
+ "requires": {
+ "minimatch": "^5.0.1"
+ }
+ },
+ "image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
+ "dev": true,
+ "optional": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true
+ },
+ "immutable": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz",
+ "integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "dependencies": {
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ }
+ }
+ },
+ "import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "requires": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==",
+ "dev": true
+ },
+ "infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz",
+ "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz",
+ "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.1",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.21",
+ "mute-stream": "0.0.8",
+ "ora": "^5.4.1",
+ "run-async": "^2.4.0",
+ "rxjs": "^7.5.5",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6",
+ "wrap-ansi": "^7.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "interpret": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
+ },
+ "ip": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
+ "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true
+ },
+ "is-lambda": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+ "dev": true
+ },
+ "is-lower-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz",
+ "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==",
+ "requires": {
+ "lower-case": "^1.1.0"
+ },
+ "dependencies": {
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ }
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ },
+ "dependencies": {
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ }
+ }
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+ "dev": true
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
+ "dev": true
+ },
+ "is-relative-path": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.2.tgz",
+ "integrity": "sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true
+ },
+ "is-upper-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz",
+ "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==",
+ "requires": {
+ "upper-case": "^1.1.0"
+ }
+ },
+ "is-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "dev": true
+ },
+ "is-url-superb": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz",
+ "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==",
+ "dev": true
+ },
+ "is-what": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+ "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "isbinaryfile": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+ "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true
+ },
+ "istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "jasmine": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.3.0.tgz",
+ "integrity": "sha512-ieBmwkd8L1DXnvSnxx7tecXgA0JDgMXPAwBcqM4lLPedJeI9hTHuWifPynTC+dLe4Y+GkSPSlbqqrmYIgGzYUw==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.6",
+ "jasmine-core": "^4.3.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "jasmine-core": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.5.0.tgz",
+ "integrity": "sha512-9PMzyvhtocxb3aXJVOPqBDswdgyAeSB81QnLop4npOpbqnheaTEwPc9ZloQeVswugPManznQBjD8kWDTjlnHuw==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "jasmine-core": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.1.tgz",
+ "integrity": "sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==",
+ "dev": true
+ },
+ "jasminewd2": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
+ "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==",
+ "dev": true
+ },
+ "jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "requires": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "jose": {
+ "version": "4.11.1",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz",
+ "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q=="
+ },
+ "js-sdsl": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
+ "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsbn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
+ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
+ },
+ "jsbn-rsa": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/jsbn-rsa/-/jsbn-rsa-1.0.4.tgz",
+ "integrity": "sha512-unHyEPFGjr6WCzrcMiwdNhYMlq4gXt6Hg5JuKOyE7OXJ7GbVMpottnqsUkPeZCAYqByAkn4N8gJwCpnacduOew=="
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
+ "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==",
+ "dev": true
+ },
+ "jsonc-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz",
+ "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true
+ },
+ "jsonwebtoken": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+ "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash": "^4.17.21",
+ "ms": "^2.1.1",
+ "semver": "^7.3.8"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
+ "jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dev": true,
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jwks-rsa": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.0.0.tgz",
+ "integrity": "sha512-x9qNrP/kD6tOfrLzBVC5HaneBTR+fCEGIjwk/xSdl+KA7Tzf+R3oiY9ibrONKVLF9fR0V03enkitYPZkO65fAQ==",
+ "requires": {
+ "@types/express": "^4.17.14",
+ "@types/jsonwebtoken": "^8.5.9",
+ "debug": "^4.3.4",
+ "jose": "^4.10.3",
+ "limiter": "^1.1.5",
+ "lru-memoizer": "^2.1.4"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "karma": {
+ "version": "6.3.20",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz",
+ "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==",
+ "dev": true,
+ "requires": {
+ "@colors/colors": "1.5.0",
+ "body-parser": "^1.19.0",
+ "braces": "^3.0.2",
+ "chokidar": "^3.5.1",
+ "connect": "^3.7.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.1",
+ "glob": "^7.1.7",
+ "graceful-fs": "^4.2.6",
+ "http-proxy": "^1.18.1",
+ "isbinaryfile": "^4.0.8",
+ "lodash": "^4.17.21",
+ "log4js": "^6.4.1",
+ "mime": "^2.5.2",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.5",
+ "qjobs": "^1.2.0",
+ "range-parser": "^1.2.1",
+ "rimraf": "^3.0.2",
+ "socket.io": "^4.4.1",
+ "source-map": "^0.6.1",
+ "tmp": "^0.2.1",
+ "ua-parser-js": "^0.7.30",
+ "yargs": "^16.1.1"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+ "dev": true,
+ "requires": {
+ "rimraf": "^3.0.0"
+ }
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true
+ }
+ }
+ },
+ "karma-chrome-launcher": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz",
+ "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==",
+ "dev": true,
+ "requires": {
+ "which": "^1.2.1"
+ },
+ "dependencies": {
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "karma-jasmine": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.0.1.tgz",
+ "integrity": "sha512-FkL1Kk+JAKmim8VWU8RXKZBpl0lLI7J8LijM0/q7oP7emfB6QMZV1Az+JgqGKSLpF0tYaav+KUVFQroZUxQTHA==",
+ "dev": true,
+ "requires": {
+ "jasmine-core": "^4.1.0"
+ }
+ },
+ "karma-source-map-support": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
+ "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
+ "dev": true,
+ "requires": {
+ "source-map-support": "^0.5.5"
+ }
+ },
+ "karma-sourcemap-loader": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz",
+ "integrity": "sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2"
+ }
+ },
+ "karma-webpack": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz",
+ "integrity": "sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3",
+ "minimatch": "^3.0.4",
+ "webpack-merge": "^4.1.5"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "webpack-merge": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
+ "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.15"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
+ },
+ "klona": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
+ "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
+ "dev": true
+ },
+ "kotlin": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/kotlin/-/kotlin-1.8.10.tgz",
+ "integrity": "sha512-vNB/4k1sv10L2GXasZwesZIV3pyEtB9MRmWyxXV4VsFD3KIJwQt81fcU2mXj/aGwKnL67zeMQpbgKU3yvfJpCw=="
+ },
+ "kotlin-compiler": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/kotlin-compiler/-/kotlin-compiler-1.8.10.tgz",
+ "integrity": "sha512-jvmEVUb0M+2kiaytLI/QIYQT1rSh5VQs62ArebSDKMu2EO7ZRatUh/cTqR7QtLlsjzI8PV156BiOIn+CmoR+2A=="
+ },
+ "less": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
+ "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==",
+ "dev": true,
+ "requires": {
+ "copy-anything": "^2.0.1",
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
+ "mime": "^1.4.1",
+ "needle": "^3.1.0",
+ "parse-node-version": "^1.0.1",
+ "source-map": "~0.6.0",
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "optional": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "optional": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "less-loader": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz",
+ "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==",
+ "dev": true,
+ "requires": {
+ "klona": "^2.0.4"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "license-webpack-plugin": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
+ "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==",
+ "dev": true,
+ "requires": {
+ "webpack-sources": "^3.0.0"
+ }
+ },
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "limiter": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
+ },
+ "lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "loader-runner": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="
+ },
+ "loader-utils": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "log4js": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.1.tgz",
+ "integrity": "sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ==",
+ "dev": true,
+ "requires": {
+ "date-format": "^4.0.14",
+ "debug": "^4.3.4",
+ "flatted": "^3.2.7",
+ "rfdc": "^1.3.0",
+ "streamroller": "^3.1.3"
+ }
+ },
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
+ "lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "requires": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "lower-case-first": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz",
+ "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==",
+ "requires": {
+ "lower-case": "^1.1.2"
+ },
+ "dependencies": {
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ }
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "lru-memoizer": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz",
+ "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==",
+ "requires": {
+ "lodash.clonedeep": "^4.5.0",
+ "lru-cache": "~4.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
+ "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==",
+ "requires": {
+ "pseudomap": "^1.0.1",
+ "yallist": "^2.0.0"
+ }
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
+ }
+ }
+ },
+ "madge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/madge/-/madge-5.0.1.tgz",
+ "integrity": "sha512-krmSWL9Hkgub74bOjnjWRoFPAJvPwSG6Dbta06qhWOq6X/n/FPzO3ESZvbFYVIvG2g4UHXvCJN1b+RZLaSs9nA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.1",
+ "commander": "^7.2.0",
+ "commondir": "^1.0.1",
+ "debug": "^4.3.1",
+ "dependency-tree": "^8.1.1",
+ "detective-amd": "^3.1.0",
+ "detective-cjs": "^3.1.1",
+ "detective-es6": "^2.2.0",
+ "detective-less": "^1.0.2",
+ "detective-postcss": "^5.0.0",
+ "detective-sass": "^3.0.1",
+ "detective-scss": "^2.0.1",
+ "detective-stylus": "^1.0.0",
+ "detective-typescript": "^7.0.0",
+ "graphviz": "0.0.9",
+ "ora": "^5.4.1",
+ "pluralize": "^8.0.0",
+ "precinct": "^8.1.0",
+ "pretty-ms": "^7.0.1",
+ "rc": "^1.2.7",
+ "typescript": "^3.9.5",
+ "walkdir": "^0.4.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "dev": true
+ }
+ }
+ },
+ "magic-string": {
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
+ "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "make-fetch-happen": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
+ "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==",
+ "dev": true,
+ "requires": {
+ "agentkeepalive": "^4.1.3",
+ "cacache": "^15.2.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.3",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^1.3.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.2",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^6.0.0",
+ "ssri": "^8.0.0"
+ },
+ "dependencies": {
+ "@npmcli/fs": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
+ "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==",
+ "dev": true,
+ "requires": {
+ "@gar/promisify": "^1.0.1",
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/move-file": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
+ "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "cacache": {
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
+ "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==",
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^1.0.0",
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "glob": "^7.1.4",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.1",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.2",
+ "mkdirp": "^1.0.3",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.1",
+ "tar": "^6.0.2",
+ "unique-filename": "^1.1.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "ssri": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
+ "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.1.1"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true
+ },
+ "memfs": {
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz",
+ "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==",
+ "dev": true,
+ "requires": {
+ "fs-monkey": "^1.0.3"
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
+ "dev": true
+ },
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "mini-css-extract-plugin": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz",
+ "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==",
+ "dev": true,
+ "requires": {
+ "schema-utils": "^4.0.0"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ }
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
+ "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true
+ },
+ "minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "minipass-collect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-fetch": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz",
+ "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.12",
+ "minipass": "^3.1.0",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.0.0"
+ }
+ },
+ "minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-json-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
+ "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.3.1",
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-sized": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+ "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ },
+ "module-definition": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-3.4.0.tgz",
+ "integrity": "sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^3.0.0",
+ "node-source-walk": "^4.0.0"
+ }
+ },
+ "module-lookup-amd": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz",
+ "integrity": "sha512-w9mCNlj0S8qviuHzpakaLVc+/7q50jl9a/kmJ/n8bmXQZgDPkQHnPBb8MUOYh3WpAYkXuNc2c+khsozhIp/amQ==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.8.1",
+ "debug": "^4.1.0",
+ "glob": "^7.1.6",
+ "requirejs": "^2.3.5",
+ "requirejs-config-file": "^4.0.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "multicast-dns": {
+ "version": "7.2.5",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
+ "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
+ "dev": true,
+ "requires": {
+ "dns-packet": "^5.2.2",
+ "thunky": "^1.0.2"
+ }
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "natural-compare-lite": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+ "dev": true
+ },
+ "needle": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz",
+ "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^3.2.6",
+ "iconv-lite": "^0.6.3",
+ "sax": "^1.2.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
+ }
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
+ "nice-napi": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
+ "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "node-addon-api": "^3.0.0",
+ "node-gyp-build": "^4.2.2"
+ }
+ },
+ "no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "requires": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node-addon-api": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
+ "dev": true,
+ "optional": true
+ },
+ "node-forge": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "dev": true
+ },
+ "node-gyp": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
+ "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
+ "dev": true,
+ "requires": {
+ "env-paths": "^2.2.0",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^9.1.0",
+ "nopt": "^5.0.0",
+ "npmlog": "^6.0.0",
+ "rimraf": "^3.0.2",
+ "semver": "^7.3.5",
+ "tar": "^6.1.2",
+ "which": "^2.0.2"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "node-gyp-build": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
+ "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
+ "dev": true,
+ "optional": true
+ },
+ "node-releases": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
+ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A=="
+ },
+ "node-source-walk": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-4.3.0.tgz",
+ "integrity": "sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.0.0"
+ }
+ },
+ "nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz",
+ "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^5.0.0",
+ "is-core-module": "^2.8.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
+ "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "npm-install-checks": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz",
+ "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.1.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+ "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.2.tgz",
+ "integrity": "sha512-v/miORuX8cndiOheW8p2moNuPJ7QhcFh9WGlTorruG8hXSA23vMTEp5hTCmDxic0nD8KHhj/NQgFuySD3GYY3g==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^5.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^4.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz",
+ "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==",
+ "dev": true,
+ "requires": {
+ "glob": "^8.0.1",
+ "ignore-walk": "^5.0.1",
+ "npm-bundled": "^2.0.0",
+ "npm-normalize-package-bin": "^2.0.0"
+ },
+ "dependencies": {
+ "npm-bundled": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz",
+ "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==",
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^2.0.0"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz",
+ "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==",
+ "dev": true
+ }
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz",
+ "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==",
+ "dev": true,
+ "requires": {
+ "npm-install-checks": "^5.0.0",
+ "npm-normalize-package-bin": "^1.0.1",
+ "npm-package-arg": "^9.0.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz",
+ "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==",
+ "dev": true,
+ "requires": {
+ "make-fetch-happen": "^10.0.6",
+ "minipass": "^3.1.6",
+ "minipass-fetch": "^2.0.3",
+ "minipass-json-stream": "^1.0.1",
+ "minizlib": "^2.1.2",
+ "npm-package-arg": "^9.0.1",
+ "proc-log": "^2.0.0"
+ },
+ "dependencies": {
+ "@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true
+ },
+ "make-fetch-happen": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
+ "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
+ "dev": true,
+ "requires": {
+ "agentkeepalive": "^4.2.1",
+ "cacache": "^16.1.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^2.0.3",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^7.0.0",
+ "ssri": "^9.0.0"
+ }
+ },
+ "minipass-fetch": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
+ "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.13",
+ "minipass": "^3.1.6",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+ "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ }
+ }
+ }
+ },
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "npmlog": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+ "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+ "dev": true,
+ "requires": {
+ "are-we-there-yet": "^3.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^4.0.3",
+ "set-blocking": "^2.0.0"
+ }
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
+ },
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "open": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
+ "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+ "dev": true,
+ "requires": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "requires": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "dev": true,
+ "requires": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+ },
+ "pacote": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.3.0.tgz",
+ "integrity": "sha512-auhJAUlfC2TALo6I0s1vFoPvVFgWGx+uz/PnIojTTgkGwlK3Np8sGJ0ghfFhiuzJXTZoTycMLk8uLskdntPbDw==",
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^3.0.0",
+ "@npmcli/installed-package-contents": "^1.0.7",
+ "@npmcli/promise-spawn": "^3.0.0",
+ "@npmcli/run-script": "^3.0.1",
+ "cacache": "^16.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "infer-owner": "^1.0.4",
+ "minipass": "^3.1.6",
+ "mkdirp": "^1.0.4",
+ "npm-package-arg": "^9.0.0",
+ "npm-packlist": "^5.0.0",
+ "npm-pick-manifest": "^7.0.0",
+ "npm-registry-fetch": "^13.0.1",
+ "proc-log": "^2.0.0",
+ "promise-retry": "^2.0.1",
+ "read-package-json": "^5.0.0",
+ "read-package-json-fast": "^2.0.3",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11"
+ }
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "requires": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "parse-ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
+ "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==",
+ "dev": true
+ },
+ "parse-node-version": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+ "dev": true
+ },
+ "parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "optional": true
+ },
+ "parse5-html-rewriting-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz",
+ "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1",
+ "parse5-sax-parser": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ }
+ }
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ }
+ }
+ },
+ "parse5-sax-parser": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz",
+ "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ }
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "requires": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "path-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz",
+ "integrity": "sha512-2snAGA6xVRqTuTPa40bn0iEpYtVK6gEqeyS/63dqpm5pGlesOv6EmRcnB9Rr6eAnAC2Wqlbz0tqgJZryttxhxg==",
+ "requires": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "piscina": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz",
+ "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==",
+ "dev": true,
+ "requires": {
+ "eventemitter-asyncresource": "^1.0.0",
+ "hdr-histogram-js": "^2.0.1",
+ "hdr-histogram-percentiles-obj": "^3.0.0",
+ "nice-napi": "^1.0.2"
+ }
+ },
+ "pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "requires": {
+ "find-up": "^4.0.0"
+ }
+ },
+ "pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.16",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
+ "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "postcss-attribute-case-insensitive": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz",
+ "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "postcss-clamp": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz",
+ "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-color-functional-notation": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz",
+ "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-color-hex-alpha": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz",
+ "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-color-rebeccapurple": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz",
+ "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-custom-media": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz",
+ "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-custom-properties": {
+ "version": "12.1.11",
+ "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz",
+ "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-custom-selectors": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz",
+ "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.4"
+ }
+ },
+ "postcss-dir-pseudo-class": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz",
+ "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "postcss-double-position-gradients": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz",
+ "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==",
+ "dev": true,
+ "requires": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-env-function": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz",
+ "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-focus-visible": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz",
+ "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.9"
+ }
+ },
+ "postcss-focus-within": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz",
+ "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.9"
+ }
+ },
+ "postcss-font-variant": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz",
+ "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-gap-properties": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz",
+ "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-image-set-function": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz",
+ "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-import": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz",
+ "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ }
+ },
+ "postcss-initial": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz",
+ "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-lab-function": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz",
+ "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==",
+ "dev": true,
+ "requires": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-loader": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz",
+ "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==",
+ "dev": true,
+ "requires": {
+ "cosmiconfig": "^7.0.0",
+ "klona": "^2.0.5",
+ "semver": "^7.3.7"
+ }
+ },
+ "postcss-logical": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz",
+ "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-media-minmax": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz",
+ "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-modules-extract-imports": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
+ "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-modules-local-by-default": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
+ "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "postcss-modules-scope": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
+ "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.4"
+ }
+ },
+ "postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.0.0"
+ }
+ },
+ "postcss-nesting": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz",
+ "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==",
+ "dev": true,
+ "requires": {
+ "@csstools/selector-specificity": "^2.0.0",
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "postcss-opacity-percentage": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz",
+ "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-overflow-shorthand": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz",
+ "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-page-break": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz",
+ "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-place": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz",
+ "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-preset-env": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz",
+ "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==",
+ "dev": true,
+ "requires": {
+ "@csstools/postcss-cascade-layers": "^1.0.5",
+ "@csstools/postcss-color-function": "^1.1.1",
+ "@csstools/postcss-font-format-keywords": "^1.0.1",
+ "@csstools/postcss-hwb-function": "^1.0.2",
+ "@csstools/postcss-ic-unit": "^1.0.1",
+ "@csstools/postcss-is-pseudo-class": "^2.0.7",
+ "@csstools/postcss-nested-calc": "^1.0.0",
+ "@csstools/postcss-normalize-display-values": "^1.0.1",
+ "@csstools/postcss-oklab-function": "^1.1.1",
+ "@csstools/postcss-progressive-custom-properties": "^1.3.0",
+ "@csstools/postcss-stepped-value-functions": "^1.0.1",
+ "@csstools/postcss-text-decoration-shorthand": "^1.0.0",
+ "@csstools/postcss-trigonometric-functions": "^1.0.2",
+ "@csstools/postcss-unset-value": "^1.0.2",
+ "autoprefixer": "^10.4.8",
+ "browserslist": "^4.21.3",
+ "css-blank-pseudo": "^3.0.3",
+ "css-has-pseudo": "^3.0.4",
+ "css-prefers-color-scheme": "^6.0.3",
+ "cssdb": "^7.0.0",
+ "postcss-attribute-case-insensitive": "^5.0.2",
+ "postcss-clamp": "^4.1.0",
+ "postcss-color-functional-notation": "^4.2.4",
+ "postcss-color-hex-alpha": "^8.0.4",
+ "postcss-color-rebeccapurple": "^7.1.1",
+ "postcss-custom-media": "^8.0.2",
+ "postcss-custom-properties": "^12.1.8",
+ "postcss-custom-selectors": "^6.0.3",
+ "postcss-dir-pseudo-class": "^6.0.5",
+ "postcss-double-position-gradients": "^3.1.2",
+ "postcss-env-function": "^4.0.6",
+ "postcss-focus-visible": "^6.0.4",
+ "postcss-focus-within": "^5.0.4",
+ "postcss-font-variant": "^5.0.0",
+ "postcss-gap-properties": "^3.0.5",
+ "postcss-image-set-function": "^4.0.7",
+ "postcss-initial": "^4.0.1",
+ "postcss-lab-function": "^4.2.1",
+ "postcss-logical": "^5.0.4",
+ "postcss-media-minmax": "^5.0.0",
+ "postcss-nesting": "^10.1.10",
+ "postcss-opacity-percentage": "^1.1.2",
+ "postcss-overflow-shorthand": "^3.0.4",
+ "postcss-page-break": "^3.0.4",
+ "postcss-place": "^7.0.5",
+ "postcss-pseudo-class-any-link": "^7.1.6",
+ "postcss-replace-overflow-wrap": "^4.0.0",
+ "postcss-selector-not": "^6.0.1",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-pseudo-class-any-link": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz",
+ "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "postcss-replace-overflow-wrap": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz",
+ "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-selector-not": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz",
+ "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.11",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
+ "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "postcss-values-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-5.0.0.tgz",
+ "integrity": "sha512-2viDDjMMrt21W2izbeiJxl3kFuD/+asgB0CBwPEgSyhCmBnDIa/y+pLaoyX+q3I3DHH0oPPL3cgjVTQvlS1Maw==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.1.4",
+ "is-url-superb": "^4.0.0",
+ "quote-unquote": "^1.0.0"
+ },
+ "dependencies": {
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "precinct": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/precinct/-/precinct-8.3.1.tgz",
+ "integrity": "sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.3",
+ "debug": "^4.3.3",
+ "detective-amd": "^3.1.0",
+ "detective-cjs": "^3.1.1",
+ "detective-es6": "^2.2.1",
+ "detective-less": "^1.0.2",
+ "detective-postcss": "^4.0.0",
+ "detective-sass": "^3.0.1",
+ "detective-scss": "^2.0.1",
+ "detective-stylus": "^1.0.0",
+ "detective-typescript": "^7.0.0",
+ "module-definition": "^3.3.1",
+ "node-source-walk": "^4.2.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "detective-postcss": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-4.0.0.tgz",
+ "integrity": "sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "is-url": "^1.2.4",
+ "postcss": "^8.1.7",
+ "postcss-values-parser": "^2.0.1"
+ }
+ },
+ "postcss-values-parser": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz",
+ "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==",
+ "dev": true,
+ "requires": {
+ "flatten": "^1.0.2",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ }
+ }
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "prettier": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz",
+ "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==",
+ "dev": true
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
+ "prettier-plugin-organize-imports": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.1.tgz",
+ "integrity": "sha512-bty7C2Ecard5EOXirtzeCAqj4FU4epeuWrQt/Z+sh8UVEpBlBZ3m3KNPz2kFu7KgRTQx/C9o4/TdquPD1jOqjQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true
+ },
+ "pretty-error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+ "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+ "requires": {
+ "lodash": "^4.17.20",
+ "renderkid": "^3.0.0"
+ }
+ },
+ "pretty-ms": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
+ "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
+ "dev": true,
+ "requires": {
+ "parse-ms": "^2.1.0"
+ }
+ },
+ "proc-log": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
+ "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+ "dev": true
+ },
+ "promise-polyfill": {
+ "version": "8.2.3",
+ "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz",
+ "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg=="
+ },
+ "promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "dev": true,
+ "requires": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "dependencies": {
+ "retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true
+ }
+ }
+ },
+ "protobufjs": {
+ "version": "6.11.3",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
+ "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ }
+ },
+ "protractor": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz",
+ "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^0.0.32",
+ "@types/selenium-webdriver": "^3.0.0",
+ "blocking-proxy": "^1.0.0",
+ "browserstack": "^1.5.1",
+ "chalk": "^1.1.3",
+ "glob": "^7.0.3",
+ "jasmine": "2.8.0",
+ "jasminewd2": "^2.1.0",
+ "q": "1.4.1",
+ "saucelabs": "^1.5.0",
+ "selenium-webdriver": "3.6.0",
+ "source-map-support": "~0.4.0",
+ "webdriver-js-extender": "2.1.0",
+ "webdriver-manager": "^12.1.7",
+ "yargs": "^15.3.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "jasmine": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
+ "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==",
+ "dev": true,
+ "requires": {
+ "exit": "^0.1.2",
+ "glob": "^7.0.6",
+ "jasmine-core": "~2.8.0"
+ }
+ },
+ "jasmine-core": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
+ "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ }
+ }
+ },
+ "y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "requires": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "dependencies": {
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true
+ }
+ }
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+ "dev": true,
+ "optional": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+ },
+ "psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ },
+ "q": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+ "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==",
+ "dev": true
+ },
+ "qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "quote-unquote": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz",
+ "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true
+ }
+ }
+ },
+ "read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^2.3.0"
+ }
+ },
+ "read-package-json": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz",
+ "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==",
+ "dev": true,
+ "requires": {
+ "glob": "^8.0.1",
+ "json-parse-even-better-errors": "^2.3.1",
+ "normalize-package-data": "^4.0.0",
+ "npm-normalize-package-bin": "^2.0.0"
+ },
+ "dependencies": {
+ "npm-normalize-package-bin": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz",
+ "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==",
+ "dev": true
+ }
+ }
+ },
+ "read-package-json-fast": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz",
+ "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==",
+ "dev": true,
+ "requires": {
+ "json-parse-even-better-errors": "^2.3.0",
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "rechoir": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+ "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+ "requires": {
+ "resolve": "^1.9.0"
+ }
+ },
+ "reflect-metadata": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+ "dev": true
+ },
+ "regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true
+ },
+ "regenerate-unicode-properties": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
+ "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.2"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.9",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
+ "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.8.4"
+ }
+ },
+ "regex-parser": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
+ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
+ "dev": true
+ },
+ "regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
+ "regexpu-core": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz",
+ "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.1.0",
+ "regjsgen": "^0.7.1",
+ "regjsparser": "^0.9.1",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.1.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz",
+ "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
+ "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+ "dev": true
+ }
+ }
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
+ },
+ "renderkid": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+ "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+ "requires": {
+ "css-select": "^4.1.3",
+ "dom-converter": "^0.2.0",
+ "htmlparser2": "^6.1.0",
+ "lodash": "^4.17.21",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "requirejs": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
+ "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
+ "dev": true
+ },
+ "requirejs-config-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz",
+ "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.0",
+ "stringify-object": "^3.2.1"
+ }
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+ "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+ "requires": {
+ "is-core-module": "^2.8.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "requires": {
+ "resolve-from": "^5.0.0"
+ }
+ },
+ "resolve-dependency-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz",
+ "integrity": "sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w==",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
+ },
+ "resolve-url-loader": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz",
+ "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==",
+ "dev": true,
+ "requires": {
+ "adjust-sourcemap-loader": "^4.0.0",
+ "convert-source-map": "^1.7.0",
+ "loader-utils": "^2.0.0",
+ "postcss": "^8.2.14",
+ "source-map": "0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "rest-facade": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/rest-facade/-/rest-facade-1.16.3.tgz",
+ "integrity": "sha512-9BQTPLiwg23XZwcWi0ys1wTizfc//0b2G3U6vBWcgqh56ozs2K6CD+Jw4DYcw3AqdPQN7jj8nzRUcUXFVGzb0Q==",
+ "requires": {
+ "change-case": "^2.3.0",
+ "deepmerge": "^3.2.0",
+ "lodash.get": "^4.4.2",
+ "superagent": "^5.1.1"
+ }
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+ "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "run-async": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "rxjs": {
+ "version": "7.5.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz",
+ "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==",
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "sass": {
+ "version": "1.57.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
+ "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
+ "sass-loader": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz",
+ "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==",
+ "dev": true,
+ "requires": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ }
+ },
+ "sass-lookup": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-3.0.0.tgz",
+ "integrity": "sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.16.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ }
+ }
+ },
+ "saucelabs": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
+ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ }
+ }
+ }
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+ "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.5",
+ "ajv": "^6.12.4",
+ "ajv-keywords": "^3.5.2"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ }
+ }
+ },
+ "select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
+ "dev": true
+ },
+ "selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "requires": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.1"
+ }
+ }
+ }
+ },
+ "selfsigned": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz",
+ "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==",
+ "dev": true,
+ "requires": {
+ "node-forge": "^1"
+ }
+ },
+ "semver": {
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
+ "send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ }
+ }
+ },
+ "sentence-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz",
+ "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==",
+ "requires": {
+ "lower-case": "^1.1.1"
+ },
+ "dependencies": {
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ }
+ }
+ },
+ "serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.18.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "requires": {
+ "kind-of": "^6.0.2"
+ }
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
+ },
+ "side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true
+ },
+ "snake-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz",
+ "integrity": "sha512-oapUKC+qulnUIN+/O7Tbl2msi9PQvJeivGN9RNbygxzI2EOY0gA96i8BJLYnGUWSLGcYtyW4YYqnGTZEySU/gg==",
+ "requires": {
+ "sentence-case": "^1.1.2"
+ }
+ },
+ "socket.io": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz",
+ "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "debug": "~4.3.2",
+ "engine.io": "~6.2.1",
+ "socket.io-adapter": "~2.4.0",
+ "socket.io-parser": "~4.2.1"
+ }
+ },
+ "socket.io-adapter": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
+ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
+ "dev": true
+ },
+ "socket.io-parser": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
+ "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
+ "dev": true,
+ "requires": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ }
+ },
+ "sockjs": {
+ "version": "0.3.24",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+ "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+ "dev": true,
+ "requires": {
+ "faye-websocket": "^0.11.3",
+ "uuid": "^8.3.2",
+ "websocket-driver": "^0.7.4"
+ }
+ },
+ "socks": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
+ "dev": true,
+ "requires": {
+ "ip": "^2.0.0",
+ "smart-buffer": "^4.2.0"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz",
+ "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ }
+ },
+ "source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true
+ },
+ "source-map-loader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz",
+ "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.6",
+ "iconv-lite": "^0.6.3",
+ "source-map-js": "^1.0.2"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
+ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==",
+ "dev": true
+ },
+ "spdy": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ }
+ },
+ "spdy-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "dependencies": {
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ }
+ }
+ },
+ "ssri": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
+ "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.1.1"
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true
+ },
+ "streamroller": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.3.tgz",
+ "integrity": "sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w==",
+ "dev": true,
+ "requires": {
+ "date-format": "^4.0.14",
+ "debug": "^4.3.4",
+ "fs-extra": "^8.1.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "stringify-object": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
+ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+ "dev": true,
+ "requires": {
+ "get-own-enumerable-property-symbols": "^3.0.0",
+ "is-obj": "^1.0.1",
+ "is-regexp": "^1.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true
+ },
+ "strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "style-loader": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
+ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
+ "requires": {}
+ },
+ "stylus": {
+ "version": "0.59.0",
+ "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz",
+ "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==",
+ "dev": true,
+ "requires": {
+ "@adobe/css-tools": "^4.0.1",
+ "debug": "^4.3.2",
+ "glob": "^7.1.6",
+ "sax": "~1.2.4",
+ "source-map": "^0.7.3"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "stylus-loader": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz",
+ "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==",
+ "dev": true,
+ "requires": {
+ "fast-glob": "^3.2.11",
+ "klona": "^2.0.5",
+ "normalize-path": "^3.0.0"
+ }
+ },
+ "stylus-lookup": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-3.0.2.tgz",
+ "integrity": "sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.8.1",
+ "debug": "^4.1.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ }
+ }
+ },
+ "superagent": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz",
+ "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==",
+ "requires": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.2",
+ "debug": "^4.1.1",
+ "fast-safe-stringify": "^2.0.7",
+ "form-data": "^3.0.0",
+ "formidable": "^1.2.2",
+ "methods": "^1.1.2",
+ "mime": "^2.4.6",
+ "qs": "^6.9.4",
+ "readable-stream": "^3.6.0",
+ "semver": "^7.3.2"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
+ },
+ "swap-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz",
+ "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==",
+ "requires": {
+ "lower-case": "^1.1.1",
+ "upper-case": "^1.1.1"
+ },
+ "dependencies": {
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA=="
+ }
+ }
+ },
+ "symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "dev": true
+ },
+ "tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
+ },
+ "tar": {
+ "version": "6.1.13",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
+ "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==",
+ "dev": true,
+ "requires": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^4.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz",
+ "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "temp": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.4.0.tgz",
+ "integrity": "sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA==",
+ "dev": true
+ },
+ "terser": {
+ "version": "5.14.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
+ "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
+ "requires": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
+ "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
+ "requires": {
+ "@jridgewell/trace-mapping": "^0.3.14",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^3.1.1",
+ "serialize-javascript": "^6.0.0",
+ "terser": "^5.14.1"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "requires": {}
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ }
+ }
+ },
+ "test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "requires": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "text-segmentation": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
+ "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+ "requires": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "three": {
+ "version": "0.143.0",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz",
+ "integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg=="
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "thunky": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+ "dev": true
+ },
+ "title-case": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz",
+ "integrity": "sha512-xYbo5Um5MBgn24xJSK+x5hZ8ehuGXTVhgx32KJCThHRHwpyIb1lmABi1DH5VvN9E7rNEquPjz//rF/tZQd7mjQ==",
+ "requires": {
+ "sentence-case": "^1.1.1",
+ "upper-case": "^1.0.3"
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true
+ },
+ "ts-loader": {
+ "version": "9.4.2",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
+ "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
+ "requires": {
+ "chalk": "^4.1.0",
+ "enhanced-resolve": "^5.0.0",
+ "micromatch": "^4.0.0",
+ "semver": "^7.3.4"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "tsconfig-paths": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+ "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ }
+ }
+ },
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
+ },
+ "tslint": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+ "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.3",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.13.0",
+ "tsutils": "^2.29.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ }
+ }
+ },
+ "tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "typed-assert": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz",
+ "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ=="
+ },
+ "ua-parser-js": {
+ "version": "0.7.32",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
+ "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
+ "dev": true
+ },
+ "unfetch": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
+ "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA=="
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+ "dev": true
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "dev": true
+ },
+ "uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==",
+ "dev": true
+ },
+ "unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+ "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true
+ },
+ "update-browserslist-db": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "upper-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+ "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA=="
+ },
+ "upper-case-first": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz",
+ "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==",
+ "requires": {
+ "upper-case": "^1.1.1"
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "utila": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+ "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true
+ },
+ "utrie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
+ "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+ "requires": {
+ "base64-arraybuffer": "^1.0.2"
+ }
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "validate-npm-package-name": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz",
+ "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==",
+ "dev": true,
+ "requires": {
+ "builtins": "^5.0.0"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==",
+ "dev": true
+ },
+ "walkdir": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+ "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==",
+ "dev": true
+ },
+ "watchpack": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
+ "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
+ "requires": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ }
+ },
+ "wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "requires": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "requires": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "webdriver-js-extender": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
+ "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
+ "dev": true,
+ "requires": {
+ "@types/selenium-webdriver": "^3.0.0",
+ "selenium-webdriver": "^3.0.1"
+ }
+ },
+ "webdriver-manager": {
+ "version": "12.1.8",
+ "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz",
+ "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==",
+ "dev": true,
+ "requires": {
+ "adm-zip": "^0.4.9",
+ "chalk": "^1.1.1",
+ "del": "^2.2.0",
+ "glob": "^7.0.3",
+ "ini": "^1.3.4",
+ "minimist": "^1.2.0",
+ "q": "^1.4.1",
+ "request": "^2.87.0",
+ "rimraf": "^2.5.2",
+ "semver": "^5.3.0",
+ "xml2js": "^0.4.17"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true
+ }
+ }
+ },
+ "webgl-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/webgl-utils/-/webgl-utils-1.0.1.tgz",
+ "integrity": "sha512-ox5xQ3YkrrOR6pCZHTOud49zzMXP9LnXzx7jIQvHOinV4FK59rGGJURw2Lq1cCTPgMVU2wAWq7e6vEVjz9FdBw=="
+ },
+ "webgl-utils.js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/webgl-utils.js/-/webgl-utils.js-1.1.0.tgz",
+ "integrity": "sha512-cmO2aPd6gR6bK/ttdk8ZIypJfZMOcTvsvXv/LxXZjAFu5TC6vXqFrZYudlPuKxVsA34Pc8Fysq2rCnflu+wuuA=="
+ },
+ "webpack": {
+ "version": "5.75.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
+ "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
+ "requires": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/wasm-edit": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.7.6",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.10.0",
+ "es-module-lexer": "^0.9.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.1.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.1.3",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "requires": {}
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ }
+ }
+ },
+ "webpack-cli": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
+ "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
+ "requires": {
+ "@discoveryjs/json-ext": "^0.5.0",
+ "@webpack-cli/configtest": "^1.2.0",
+ "@webpack-cli/info": "^1.5.0",
+ "@webpack-cli/serve": "^1.7.0",
+ "colorette": "^2.0.14",
+ "commander": "^7.0.0",
+ "cross-spawn": "^7.0.3",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^2.2.0",
+ "rechoir": "^0.7.0",
+ "webpack-merge": "^5.7.3"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ }
+ }
+ },
+ "webpack-dev-middleware": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
+ "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
+ "dev": true,
+ "requires": {
+ "colorette": "^2.0.10",
+ "memfs": "^3.4.3",
+ "mime-types": "^2.1.31",
+ "range-parser": "^1.2.1",
+ "schema-utils": "^4.0.0"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ }
+ }
+ },
+ "webpack-dev-server": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz",
+ "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==",
+ "dev": true,
+ "requires": {
+ "@types/bonjour": "^3.5.9",
+ "@types/connect-history-api-fallback": "^1.3.5",
+ "@types/express": "^4.17.13",
+ "@types/serve-index": "^1.9.1",
+ "@types/serve-static": "^1.13.10",
+ "@types/sockjs": "^0.3.33",
+ "@types/ws": "^8.5.1",
+ "ansi-html-community": "^0.0.8",
+ "bonjour-service": "^1.0.11",
+ "chokidar": "^3.5.3",
+ "colorette": "^2.0.10",
+ "compression": "^1.7.4",
+ "connect-history-api-fallback": "^2.0.0",
+ "default-gateway": "^6.0.3",
+ "express": "^4.17.3",
+ "graceful-fs": "^4.2.6",
+ "html-entities": "^2.3.2",
+ "http-proxy-middleware": "^2.0.3",
+ "ipaddr.js": "^2.0.1",
+ "open": "^8.0.9",
+ "p-retry": "^4.5.0",
+ "rimraf": "^3.0.2",
+ "schema-utils": "^4.0.0",
+ "selfsigned": "^2.0.1",
+ "serve-index": "^1.9.1",
+ "sockjs": "^0.3.24",
+ "spdy": "^4.0.2",
+ "webpack-dev-middleware": "^5.3.1",
+ "ws": "^8.4.2"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ },
+ "ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+ },
+ "webpack-merge": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+ "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "requires": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ }
+ },
+ "webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="
+ },
+ "webpack-subresource-integrity": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz",
+ "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==",
+ "dev": true,
+ "requires": {
+ "typed-assert": "^1.0.8"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "wildcard": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "ws": {
+ "version": "8.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+ "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+ "dev": true,
+ "requires": {}
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dev": true,
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "17.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
+ "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
+ "dev": true,
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.0.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
+ },
+ "zone.js": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz",
+ "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ }
+ }
+}
diff --git a/tools/winscope/package.json b/tools/winscope/package.json
index bf23983..ef27d79 100644
--- a/tools/winscope/package.json
+++ b/tools/winscope/package.json
@@ -1,72 +1,101 @@
{
"name": "winscope",
- "description": "A window manager analysis tool",
- "version": "0.0.1",
- "author": "Adrian Roos <roosa@google.com>",
- "private": true,
+ "version": "0.0.0",
"scripts": {
- "dev": "cross-env NODE_ENV=development webpack serve --open --hot",
- "build": "cross-env NODE_ENV=production webpack --progress",
- "test": "webpack --config webpack.spec.config.js && jasmine dist/bundleSpec.js"
+ "format:check": "(ls *.js .*.js && find src/ -regextype egrep -regex '^.*(\\.ts|\\.js)$') |xargs npx prettier --check",
+ "format:fix": "(ls *.js .*.js && find src/ -regextype egrep -regex '^.*(\\.ts|\\.js)$') |xargs npx prettier --write",
+ "eslint:check": "(ls *.js .*.js && find src/ -regextype egrep -regex '^.*(\\.ts|\\.js)$') |xargs npx eslint --format=unix",
+ "eslint:fix": "(ls *.js .*.js && find src/ -regextype egrep -regex '^.*(\\.ts|\\.js)$') |xargs npx eslint --format=unix --fix",
+ "tslint:check": "find src/ -regextype egrep -regex '^.*\\.ts$' |xargs npx tslint -c google.tslint.json",
+ "tslint:fix": "find src/ -regextype egrep -regex '^.*\\.ts$' |xargs npx tslint -c google.tslint.json --fix",
+ "deps_graph:check_cycles": "count=$(npx madge --extensions ts,js src/ --circular 2>&1 | awk '/Found.*circular dependencies/ {print $3}'); test ${count:-0} -le 10",
+ "start": "webpack serve --config webpack.config.dev.js --open --hot --port 8080",
+ "start:remote_tool_mock": "webpack serve --config src/test/remote_tool_mock/webpack.config.js --open --hot --port 8081",
+ "build:kotlin_legacy": "rm -rf kotlin_build && JAVA_OPTS='-Xmx2g -Xms1g' npx kotlinc-js -source-map -source-map-embed-sources always -module-kind commonjs -output kotlin_build/flicker.js ../../../platform_testing/libraries/flicker/src/android/tools/common",
+ "build:kotlin": "rm -rf kotlin_build && mkdir kotlin_build && JAVA_OPTS='-Xmx2g -Xms1g' npx kotlinc-js -Xir-produce-js -Xir-only -Xir-module-name=flicker -Xtyped-arrays -source-map -source-map-embed-sources always -module-kind commonjs -target v8 -libraries ./node_modules/kotlin-compiler/lib/kotlin-stdlib-js.jar -output kotlin_build/flicker.js ../../../platform_testing/libraries/flicker/src/android/tools/common/",
+ "build:prod": "webpack --config webpack.config.prod.js --progress",
+ "build:remote_tool_mock": "webpack --config src/test/remote_tool_mock/webpack.config.js --progress",
+ "build:all": "npm run build:kotlin && npm run build:prod && npm run build:remote_tool_mock",
+ "test:unit": "webpack --config webpack.config.unit_test.js && jasmine dist/unit_test/bundle.js",
+ "test:component": "npx karma start",
+ "test:e2e": "rm -rf dist/e2e_test && npx tsc -p ./src/test/e2e && npx protractor protractor.config.js",
+ "test:presubmit": "npm run test:unit && npm run test:component && npm run format:check && npm run tslint:check && npm run eslint:check && npm run deps_graph:check_cycles",
+ "test:all": "npm run test:unit && npm run test:component && npm run test:e2e && npm run format:check && npm run tslint:check && npm run eslint:check && npm run deps_graph:check_cycles"
},
+ "private": true,
"dependencies": {
- "cross-env": "^7.0.3",
- "jszip": "^3.6.0",
- "kotlin": "^1.5.21",
- "lodash.clonedeep": "^4.5.0",
- "ts-loader": "^8.3.0",
- "typescript": "^4.3.5",
- "vue": "^2.6.14",
- "vue-context": "^6.0.0",
- "vue-gtag": "^1.16.1",
- "vue-material": "^1.0.0-beta-15",
- "vuex": "^3.6.2"
+ "@angular/animations": "^14.0.0",
+ "@angular/cdk": "^14.2.6",
+ "@angular/common": "^14.0.0",
+ "@angular/compiler": "^14.0.0",
+ "@angular/core": "^14.0.1",
+ "@angular/elements": "^14.0.1",
+ "@angular/forms": "^14.0.0",
+ "@angular/material": "^14.2.6",
+ "@angular/platform-browser": "^14.0.0",
+ "@angular/platform-browser-dynamic": "^14.0.0",
+ "@angular/router": "^14.0.0",
+ "@auth0/auth0-angular": "^1.10.0",
+ "@ngrx/effects": "^14.0.2",
+ "@ngrx/store": "^14.0.2",
+ "@ngxs/store": "^3.7.4",
+ "auth0": "^3.0.1",
+ "dateformat": "^5.0.3",
+ "gl-matrix": "^3.4.3",
+ "html-loader": "^3.1.0",
+ "html-webpack-inline-source-plugin": "^1.0.0-beta.2",
+ "html-webpack-plugin": "^5.5.0",
+ "html2canvas": "^1.4.1",
+ "jsbn": "^1.1.0",
+ "jsbn-rsa": "^1.0.4",
+ "kotlin": "^1.8.10",
+ "kotlin-compiler": "^1.8.10",
+ "protobufjs": "^6.11.3",
+ "rxjs": "~7.5.0",
+ "style-loader": "^3.3.1",
+ "three": "^0.143.0",
+ "ts-loader": "^9.3.0",
+ "tslib": "^2.3.0",
+ "typescript": "~4.7.2",
+ "webgl-utils": "^1.0.1",
+ "webgl-utils.js": "^1.1.0",
+ "webpack-cli": "^4.10.0",
+ "zone.js": "~0.11.4"
},
"devDependencies": {
- "@babel/core": "^7.14.6",
- "@babel/polyfill": "^7.12.1",
- "@babel/preset-env": "^7.14.7",
- "@babel/register": "^7.14.5",
- "@jetbrains/kotlin-webpack-plugin": "^3.0.2",
- "@testing-library/vue": "^5.8.1",
- "@types/lodash": "^4.14.171",
- "babel-loader": "^8.2.2",
- "compression-webpack-plugin": "^6.1.1",
- "cross-env": "^7.0.3",
- "css-loader": "^5.2.7",
- "eslint": "^7.30.0",
- "eslint-config-google": "^0.14.0",
- "eslint-plugin-vue": "^7.13.0",
- "file-loader": "^6.2.0",
- "friendly-errors-webpack-plugin": "^1.7.0",
- "html-webpack-inline-source-plugin": "^1.0.0-beta.2",
- "html-webpack-plugin": "4.5.2",
- "husky": "^7.0.0",
- "jasmine": "^3.8.0",
- "lint-staged": "^11.0.1",
+ "@angular-devkit/build-angular": "^14.0.0",
+ "@angular/cli": "~14.0.0",
+ "@angular/compiler-cli": "^14.0.0",
+ "@ngxs/devtools-plugin": "^3.7.4",
+ "@types/chrome": "^0.0.204",
+ "@types/dateformat": "^5.0.0",
+ "@types/jasmine": "~4.3.1",
+ "@types/jquery": "^3.5.14",
+ "@types/jsbn": "^1.2.30",
+ "@types/node": "^18.0.4",
+ "@types/three": "^0.143.0",
+ "@types/w3c-web-usb": "^1.0.6",
+ "@typescript-eslint/eslint-plugin": "^5.30.6",
+ "@typescript-eslint/parser": "^5.30.6",
+ "angular2-template-loader": "^0.6.2",
+ "eslint": "^8.19.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "jasmine": "~4.3.0",
+ "jasmine-core": "~4.1.0",
+ "karma": "~6.3.0",
+ "karma-chrome-launcher": "~3.1.0",
+ "karma-jasmine": "~5.0.0",
+ "karma-sourcemap-loader": "^0.3.8",
+ "karma-webpack": "^5.0.0",
"loader-utils": "^2.0.0",
- "mini-css-extract-plugin": "^1.6.2",
- "optimize-css-assets-webpack-plugin": "^5.0.3",
- "protobufjs": "^6.11.2",
- "source-map-loader": "^1.1.3",
- "style-loader": "^2.0.0",
- "ts-loader": "^8.3.0",
- "typescript": "^4.3.5",
- "uglifyjs-webpack-plugin": "^2.2.0",
- "vue-loader": "^15.9.2",
- "vue-style-loader": "^4.1.3",
- "vue-template-compiler": "^2.6.14",
- "webpack": "^4.46.0",
- "webpack-cli": "^4.7.2",
- "webpack-dev-server": "^3.11.2",
- "webpack-merge": "^5.8.0"
- },
- "husky": {
- "hooks": {
- "pre-commit": "lint-staged"
- }
- },
- "lint-staged": {
- "*.{js,vue}": "eslint --cache --fix"
+ "madge": "^5.0.1",
+ "prettier": "^2.8.1",
+ "prettier-plugin-organize-imports": "^3.2.1",
+ "protractor": "^7.0.0",
+ "sass": "^1.56.1",
+ "sass-loader": "^13.1.0",
+ "tslint": "^6.1.3",
+ "webpack": "^5.74.0"
}
}
diff --git a/tools/winscope/prettier.config.js b/tools/winscope/prettier.config.js
new file mode 100644
index 0000000..72fd720
--- /dev/null
+++ b/tools/winscope/prettier.config.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 = {
+ arrowParens: 'always',
+ bracketSameLine: true,
+ bracketSpacing: false,
+ printWidth: 100,
+ singleQuote: true,
+ semi: true,
+ tabWidth: 2,
+ useTabs: false,
+ trailingComma: 'es5',
+ //enable this setting in case prettier-plugin-organize-imports starts breaking stuff
+ //organizeImportsSkipDestructiveCodeActions: true,
+};
diff --git a/tools/winscope/protractor.config.js b/tools/winscope/protractor.config.js
new file mode 100644
index 0000000..f1fdbe6
--- /dev/null
+++ b/tools/winscope/protractor.config.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// Note:
+// Chrome driver must match the system's Chrome browser version.
+// Use this command to update to the specified Chrome driver version:
+// node node_modules/.bin/webdriver-manager update -- versions.chrome=<NEW VERSION>
+// and change the hardcoded version here
+
+exports.config = {
+ specs: ['dist/e2e_test/e2e/*_test.js'],
+
+ directConnect: true,
+ capabilities: {
+ browserName: 'chrome',
+ chromeOptions: {
+ args: ['--headless', '--disable-gpu', '--window-size=1280x1024'],
+ },
+ },
+ chromeDriver: './node_modules/webdriver-manager/selenium/chromedriver_113.0.5672.63',
+
+ allScriptsTimeout: 10000,
+ getPageTimeout: 10000,
+
+ jasmineNodeOpts: {
+ defaultTimeoutInterval: 10000,
+ },
+
+ onPrepare: function () {
+ // allow specifying the file protocol within browser.get(...)
+ browser.ignoreSynchronization = true;
+ browser.waitForAngular();
+ browser.sleep(500);
+ browser.resetUrl = 'file:///';
+ },
+};
diff --git a/tools/winscope/spec/DiffSpec.js b/tools/winscope/spec/DiffSpec.js
deleted file mode 100644
index eb767fa..0000000
--- a/tools/winscope/spec/DiffSpec.js
+++ /dev/null
@@ -1,247 +0,0 @@
-import { DiffGenerator, DiffType } from "../src/utils/diff.js";
-import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
-
-const treeOne = new Node({ id: 1 }, [
- new Node({ id: 2 }, []),
- new Node({ id: 3 }, []),
- new Node({ id: 4 }, []),
-]);
-const treeTwo = new Node({ id: 1 }, [
- new Node({ id: 2 }, []),
- new Node({ id: 3 }, [
- new Node({ id: 5 }, []),
- ]),
- new Node({ id: 4 }, []),
-]);
-
-function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
- const diffTree = new DiffGenerator(newTree)
- .compareWith(oldTree)
- .withUniqueNodeId(node => node.id)
- .withModifiedCheck(() => false)
- .generateDiffTree();
-
- expect(diffTree).toEqual(expectedDiffTree);
-}
-
-describe("DiffGenerator", () => {
- it("can generate a simple add diff", () => {
- const oldTree = treeOne;
- const newTree = treeTwo;
-
- 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 = treeTwo;
- const newTree = treeOne;
-
- 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 = treeTwo;
-
- 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
deleted file mode 100644
index ea1d475..0000000
--- a/tools/winscope/spec/ObjectTransformerSpec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import { DiffType } from "../src/utils/diff.js";
-import { ObjectTransformer } from "../src/transform.js";
-import { ObjNode, ObjDiffNode, toPlainObject } from "./utils/tree.js";
-
-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, 'root.obj.string'),
- new ObjNode('number: 3', [], true, 'root.obj.number'),
- ], undefined, 'root.obj'),
- new ObjNode('array', [
- new ObjNode('0', [
- new ObjNode('nested: item', [], true, 'root.array.0.nested'),
- ], undefined, 'root.array.0'),
- new ObjNode("1: two", [], true, 'root.array.1'),
- ], undefined, 'root.array'),
- ], undefined, 'root')
- );
-
- const transformedObj = new ObjectTransformer(obj, 'root', '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, 'root.obj.null'),
- ], undefined, 'root.obj'),
- ], undefined, 'root')
- );
-
- const transformedObj = new ObjectTransformer(obj, 'root', '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, 'root.a.b'),
- new ObjDiffNode('d: 3', DiffType.ADDED, [], true, 'root.a.d'),
- ], false, 'root.a'),
- new ObjDiffNode('c: 2', DiffType.NONE, [], true, 'root.c'),
- ], false, 'root')
- );
-
- const transformedObj = new ObjectTransformer(newObj, 'root', '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, [], false, 'root.a.1'),
- new ObjDiffNode('null', DiffType.DELETED, [], false, 'root.a.null'),
- ], false, 'root.a'),
- ], false, 'root')
- );
-
- const transformedObj = new ObjectTransformer(newObj, 'root', '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, [], false, 'root.a.b.1'),
- new ObjDiffNode('null', DiffType.DELETED, [], false, 'root.a.b.null'),
- ], false, 'root.a.b'),
- ], false, 'root.a'),
- new ObjDiffNode('c: 2', DiffType.NONE, [], true, 'root.c'),
- ], false, 'root')
- );
-
- const transformedObj = new ObjectTransformer(newObj, 'root', '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
deleted file mode 100644
index 0188d24..0000000
--- a/tools/winscope/spec/ProtoTransformSpec.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { decodeAndTransformProto, FILE_TYPES, FILE_DECODERS } from '../src/decode';
-import fs from 'fs';
-import path from 'path';
-import { expectedEntries, expectedLayers, layers_traces } from './traces/ExpectedTraces';
-
-describe("Proto Transformations", () => {
- it("can transform surface flinger traces", () => {
- for (var i = 0; i < layers_traces.length; i++) {
- const trace = layers_traces[i];
- const buffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, trace)));
- const data = decodeAndTransformProto(buffer, FILE_DECODERS[FILE_TYPES.SURFACE_FLINGER_TRACE].decoderParams, true);
-
- // use final entry as this determines if there was any error in previous entry parsing
- const transformedEntry = data.entries[data.entries.length-1];
- const expectedEntry = expectedEntries[i];
- for (const property in expectedEntry) {
- expect(transformedEntry[property]).toEqual(expectedEntry[property]);
- }
-
- // check final flattened layer
- const transformedLayer = transformedEntry.flattenedLayers[transformedEntry.flattenedLayers.length-1];
- const expectedLayer = expectedLayers[i];
- for (const property in expectedLayer) {
- expect(transformedLayer[property]).toEqual(expectedLayer[property]);
- }
- }
- });
-});
\ No newline at end of file
diff --git a/tools/winscope/spec/SimplifiedLayerNamesSpec.js b/tools/winscope/spec/SimplifiedLayerNamesSpec.js
deleted file mode 100644
index ce2f83f..0000000
--- a/tools/winscope/spec/SimplifiedLayerNamesSpec.js
+++ /dev/null
@@ -1,24 +0,0 @@
-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/TagErrorSpec.js b/tools/winscope/spec/TagErrorSpec.js
deleted file mode 100644
index aa1ea47..0000000
--- a/tools/winscope/spec/TagErrorSpec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { decodeAndTransformProto, FILE_TYPES, FILE_DECODERS } from '../src/decode';
-import Tag from '../src/flickerlib/tags/Tag';
-import Error from '../src/flickerlib/errors/Error';
-import { TaggingEngine } from '../src/flickerlib/common.js';
-import fs from 'fs';
-import path from 'path';
-
-const tagTrace = '../spec/traces/tag_trace.winscope';
-const errorTrace = '../spec/traces/error_trace.winscope';
-
-describe("Tag Transformation", () => {
- it("can transform tag traces", () => {
- const buffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, tagTrace)));
-
- const data = decodeAndTransformProto(buffer, FILE_DECODERS[FILE_TYPES.TAG_TRACE].decoderParams, true);
-
- expect(data.entries[0].timestamp.toString()).toEqual('159979677861');
- expect(data.entries[1].timestamp.toString()).toEqual('161268519083');
- expect(data.entries[2].timestamp.toString()).toEqual('161126718496');
- expect(data.entries[3].timestamp.toString()).toEqual('161613497398');
- expect(data.entries[4].timestamp.toString()).toEqual('161227062777');
- expect(data.entries[5].timestamp.toString()).toEqual('161268519083');
- expect(data.entries[6].timestamp.toString()).toEqual('161825945076');
- expect(data.entries[7].timestamp.toString()).toEqual('162261072567');
-
- expect(data.entries[0].tags).toEqual([new Tag(12345,"PIP_ENTER",true,1,"",0)]);
- expect(data.entries[1].tags).toEqual([new Tag(12345,"PIP_ENTER",false,2,"",2)]);
- expect(data.entries[2].tags).toEqual([new Tag(67890,"ROTATION",true,3,"",3)]);
- expect(data.entries[3].tags).toEqual([new Tag(67890,"ROTATION",false,4,"",4)]);
- expect(data.entries[4].tags).toEqual([new Tag(9876,"PIP_EXIT",true,5,"",5)]);
- expect(data.entries[5].tags).toEqual([new Tag(9876,"PIP_EXIT",false,6,"",6)]);
- expect(data.entries[6].tags).toEqual([new Tag(54321,"IME_APPEAR",true,7,"",7)]);
- expect(data.entries[7].tags).toEqual([new Tag(54321,"IME_APPEAR",false,8,"",8)]);
- })
-});
-
-describe("Detect Tag", () => {
- it("can detect tags", () => {
- const wmFile = '../spec/traces/regular_rotation_in_last_state_wm_trace.winscope'
- const layersFile = '../spec/traces/regular_rotation_in_last_state_layers_trace.winscope'
- const wmBuffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, wmFile)));
- const layersBuffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, layersFile)));
-
- const wmTrace = decodeAndTransformProto(wmBuffer, FILE_DECODERS[FILE_TYPES.WINDOW_MANAGER_TRACE].decoderParams, true);
- const layersTrace = decodeAndTransformProto(layersBuffer, FILE_DECODERS[FILE_TYPES.SURFACE_FLINGER_TRACE].decoderParams, true);
-
- const engine = new TaggingEngine(wmTrace, layersTrace, (text) => { console.log(text) });
- const tagTrace = engine.run();
- expect(tagTrace.size).toEqual(4);
- expect(tagTrace.entries[0].timestamp.toString()).toEqual('280186737540384');
- expect(tagTrace.entries[1].timestamp.toString()).toEqual('280187243649340');
- expect(tagTrace.entries[2].timestamp.toString()).toEqual('280188522078113');
- expect(tagTrace.entries[3].timestamp.toString()).toEqual('280189020672174');
- })
-});
-
-describe("Error Transformation", () => {
- it("can transform error traces", () => {
- const buffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, errorTrace)));
-
- const data = decodeAndTransformProto(buffer, FILE_DECODERS[FILE_TYPES.ERROR_TRACE].decoderParams, true);
-
- expect(data.entries[0].timestamp.toString()).toEqual('161401263106');
- expect(data.entries[1].timestamp.toString()).toEqual('161126718496');
- expect(data.entries[2].timestamp.toString()).toEqual('162261072567');
-
- expect(data.entries[0].errors).toEqual([new Error("","",33,"",33)]);
- expect(data.entries[1].errors).toEqual([new Error("","",66,"",66)]);
- expect(data.entries[2].errors).toEqual([new Error("","",99,"",99)]);
- })
-});
\ No newline at end of file
diff --git a/tools/winscope/spec/support/jasmine.json b/tools/winscope/spec/support/jasmine.json
deleted file mode 100644
index 32579a5..0000000
--- a/tools/winscope/spec/support/jasmine.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "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/ExpectedTraces.js b/tools/winscope/spec/traces/ExpectedTraces.js
deleted file mode 100644
index 04a8154..0000000
--- a/tools/winscope/spec/traces/ExpectedTraces.js
+++ /dev/null
@@ -1,492 +0,0 @@
-import { ActiveBuffer, RectF, Transform, Matrix33, Color, Rect, Region } from '../../src/flickerlib/common.js';
-import { VISIBLE_CHIP } from '../../src/flickerlib/treeview/Chips';
-
-const standardTransform = new Transform(0, new Matrix33(1, 0, 0, 0, 1, 0));
-const standardRect = new Rect(0, 0, 0, 0);
-const standardColor = new Color(0, 0, 0, 1);
-const standardCrop = new Rect(0, 0, -1, -1);
-
-const expectedEmptyRegionLayer = {
- backgroundBlurRadius: 0,
- chips: [],
- cornerRadius: 0,
- effectiveScalingMode: 0,
- hwcCompositionType: "INVALID",
- id: 580,
- isOpaque: false,
- isRelativeOf: false,
- kind: "580",
- name: "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
- shadowRadius: 0,
- shortName: "SurfaceView - com.android.(...).Main#0",
- type: "BufferLayer",
- z: -1,
- zOrderRelativeOf: null,
- parentId: 579,
- activeBuffer: new ActiveBuffer(1440, 2614, 1472, 1),
- bufferTransform: standardTransform,
- color: new Color(0, 0, 0, 0.0069580078125),
- crop: standardCrop,
- hwcFrame: standardRect,
- screenBounds: new RectF(37, 43, 146, 152),
- transform: new Transform(0, new Matrix33(1, 0, 37.37078094482422, 0, 1, -3.5995326042175293)),
- visibleRegion: new Region([new Rect(37, 43, 146, 152)]),
-};
-const emptyRegionProto = {
- 2: "\nparent=0\ntype=BufferLayer\nname=Display Root#0",
- 3: "\nparent=0\ntype=BufferLayer\nname=Display Overlays#0",
- 4: "\nparent=2\ntype=BufferLayer\nname=mBelowAppWindowsContainers#0",
- 5: "\nparent=2\ntype=BufferLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@193aa46#0",
- 6: "\nparent=5\ntype=BufferLayer\nname=animationLayer#0",
- 7: "\nparent=5\ntype=BufferLayer\nname=splitScreenDividerAnchor#0",
- 8: "\nparent=2\ntype=BufferLayer\nname=mAboveAppWindowsContainers#0",
- 9: "\nparent=2\ntype=BufferLayer\nname=mImeWindowsContainers#0",
- 10: "\nparent=5\ntype=BufferLayer\nname=Stack=0#0",
- 11: "\nparent=10\ntype=ColorLayer\nname=animation background stackId=0#0",
- 12: "\nparent=9\ntype=BufferLayer\nname=WindowToken{f81e7fc android.os.Binder@7c880ef}#0",
- 13: "\nparent=4\ntype=BufferLayer\nname=WallpaperWindowToken{3756850 token=android.os.Binder@25b3e13}#0",
- 18: "\nparent=13\ntype=BufferLayer\nname=fd46a8e com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
- 19: "\nparent=18\ntype=BufferLayer\nname=com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
- 20: "\nparent=8\ntype=BufferLayer\nname=WindowToken{fc1aa98 android.os.BinderProxy@3517c7b}#0",
- 21: "\nparent=20\ntype=BufferLayer\nname=10022f1 DockedStackDivider#0",
- 22: "\nparent=8\ntype=BufferLayer\nname=WindowToken{49a6772 android.os.BinderProxy@7ba1c7d}#0",
- 23: "\nparent=22\ntype=BufferLayer\nname=56ef7c3 AssistPreviewPanel#0",
- 24: "\nparent=8\ntype=BufferLayer\nname=WindowToken{35f7d5c android.os.BinderProxy@8b38fcf}#0",
- 25: "\nparent=24\ntype=BufferLayer\nname=9029865 NavigationBar#0",
- 26: "\nparent=8\ntype=BufferLayer\nname=WindowToken{a9a69ab android.os.BinderProxy@f64ffa}#0",
- 27: "\nparent=26\ntype=BufferLayer\nname=5334808 StatusBar#0",
- 28: "\nparent=8\ntype=BufferLayer\nname=WindowToken{a63ca37 android.os.BinderProxy@435eb36}#0",
- 29: "\nparent=28\ntype=BufferLayer\nname=1a40ba4 ScreenDecorOverlay#0",
- 30: "\nparent=8\ntype=BufferLayer\nname=WindowToken{4ed84c2 android.os.BinderProxy@33d1d0d}#0",
- 31: "\nparent=30\ntype=BufferLayer\nname=7a0d2d3 ScreenDecorOverlayBottom#0",
- 32: "\nparent=25\ntype=BufferLayer\nname=NavigationBar#0",
- 33: "\nparent=27\ntype=BufferLayer\nname=StatusBar#0",
- 34: "\nparent=29\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
- 35: "\nparent=31\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
- 36: "\nparent=10\ntype=BufferLayer\nname=Task=239#0",
- 37: "\nparent=632\ntype=BufferLayer\nname=AppWindowToken{188ce21 token=Token{824488 ActivityRecord{b0d882b u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t239}}}#0",
- 38: "\nparent=37\ntype=BufferLayer\nname=9f6e33d com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 44: "\nparent=37\ntype=BufferLayer\nname=81a00fc com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 88: "\nparent=5\ntype=BufferLayer\nname=Stack=2#0",
- 89: "\nparent=88\ntype=ColorLayer\nname=animation background stackId=2#0",
- 90: "\nparent=88\ntype=BufferLayer\nname=Task=241#0",
- 91: "\nparent=633\ntype=BufferLayer\nname=AppWindowToken{a9f5144 token=Token{f102257 ActivityRecord{3a0fd6 u0 com.android.chrome/com.google.android.apps.chrome.Main t241}}}#0",
- 96: "\nparent=91\ntype=BufferLayer\nname=87e310e com.android.chrome/com.google.android.apps.chrome.Main#0",
- 574: "\nparent=8\ntype=BufferLayer\nname=WindowToken{37eed7d android.os.Binder@6e217d4}#0",
- 579: "\nparent=96\ntype=BufferLayer\nname=com.android.chrome/com.google.android.apps.chrome.Main#0",
- 580: "\nparent=579\ntype=BufferLayer\nname=SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
- 581: "\nparent=579\ntype=ColorLayer\nname=Background for -SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
- 583: "\nparent=44\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 629: "\nparent=38\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
- 632: "\nparent=6\ntype=BufferLayer\nname=Surface(name=AppWindowToken{188ce21 token=Token{824488 ActivityRecord{b0d882b u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t239}}})/@0x90c9c46 - animation-leash#1",
- 633: "\nparent=6\ntype=BufferLayer\nname=Surface(name=AppWindowToken{a9f5144 token=Token{f102257 ActivityRecord{3a0fd6 u0 com.android.chrome/com.google.android.apps.chrome.Main t241}}})/@0xd9b9374 - animation-leash#1"
-};
-const expectedEmptyRegion = {
- chips: [],
- proto: emptyRegionProto,
- hwcBlob: "",
- isVisible: true,
- kind: "entry",
- rects: [],
- shortName: "0d0h38m28s521ms",
- timestampMs: "2308521813510",
- where: "",
- name: "0d0h38m28s521ms",
- stableId: "LayerTraceEntry",
- visibleLayers: [],
-};
-
-const expectedInvalidLayerVisibilityLayer = {
- backgroundBlurRadius: 0,
- chips: [],
- cornerRadius: 0,
- effectiveScalingMode: 0,
- hwcCompositionType: "INVALID",
- id: 1536,
- isOpaque: false,
- isRelativeOf: false,
- kind: "1536",
- name: "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
- shadowRadius: 0,
- shortName: "com.google.(...).NexusLauncherActivity#2",
- type: "BufferLayer",
- z: 0,
- zOrderRelativeOf: null,
- parentId: 1535,
- stableId: "BufferLayer 1536 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
- activeBuffer: new ActiveBuffer(1440, 2880, 1472, 1),
- bufferTransform: standardTransform,
- color: new Color(-1, -1, -1, 0),
- hwcFrame: standardRect,
- transform: standardTransform,
- visibleRegion: new Region([standardRect]),
-};
-const invalidLayerVisibilityProto = {
- 2: "\nparent=0\ntype=BufferLayer\nname=Display Root#0",
- 3: "\nparent=0\ntype=BufferLayer\nname=Display Overlays#0",
- 4: "\nparent=2\ntype=BufferLayer\nname=mBelowAppWindowsContainers#0",
- 5: "\nparent=2\ntype=BufferLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@4270eb4#0",
- 6: "\nparent=5\ntype=BufferLayer\nname=animationLayer#0",
- 7: "\nparent=5\ntype=BufferLayer\nname=boostedAnimationLayer#0",
- 8: "\nparent=5\ntype=BufferLayer\nname=homeAnimationLayer#0",
- 9: "\nparent=5\ntype=BufferLayer\nname=splitScreenDividerAnchor#0",
- 10: "\nparent=2\ntype=BufferLayer\nname=mAboveAppWindowsContainers#0",
- 11: "\nparent=2\ntype=BufferLayer\nname=mImeWindowsContainers#0",
- 12: "\nparent=5\ntype=BufferLayer\nname=Stack=0#0",
- 13: "\nparent=12\ntype=ColorLayer\nname=animation background stackId=0#0",
- 14: "\nparent=11\ntype=BufferLayer\nname=WindowToken{268fcff android.os.Binder@6688c1e}#0",
- 15: "\nparent=4\ntype=BufferLayer\nname=WallpaperWindowToken{6572e20 token=android.os.Binder@9543923}#0",
- 20: "\nparent=15\ntype=BufferLayer\nname=5e2e96f com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
- 21: "\nparent=20\ntype=BufferLayer\nname=com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
- 26: "\nparent=10\ntype=BufferLayer\nname=WindowToken{68e3f31 android.os.BinderProxy@f018fd8}#0",
- 27: "\nparent=26\ntype=BufferLayer\nname=2b80616 NavigationBar#0",
- 28: "\nparent=10\ntype=BufferLayer\nname=WindowToken{4e20cae android.os.BinderProxy@9086129}#0",
- 29: "\nparent=28\ntype=BufferLayer\nname=b09a4f StatusBar#0",
- 30: "\nparent=3\ntype=BufferLayer\nname=WindowToken{501e3b8 android.os.BinderProxy@238661b}#0",
- 31: "\nparent=30\ntype=BufferLayer\nname=d803591 ScreenDecorOverlay#0",
- 32: "\nparent=3\ntype=BufferLayer\nname=WindowToken{56d48f7 android.os.BinderProxy@f0f2cf6}#0",
- 33: "\nparent=32\ntype=BufferLayer\nname=1cd8364 ScreenDecorOverlayBottom#0",
- 35: "\nparent=29\ntype=BufferLayer\nname=StatusBar#0",
- 36: "\nparent=31\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
- 37: "\nparent=33\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
- 38: "\nparent=12\ntype=BufferLayer\nname=Task=2#0",
- 39: "\nparent=38\ntype=BufferLayer\nname=AppWindowToken{215b919 token=Token{104a060 ActivityRecord{7e30c63 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t2}}}#0",
- 821: "\nparent=14\ntype=BufferLayer\nname=5c937c8 InputMethod#0",
- 1078: "\nparent=10\ntype=BufferLayer\nname=WindowToken{7dc6283 android.os.BinderProxy@f83c532}#0",
- 1079: "\nparent=1078\ntype=BufferLayer\nname=32c0c00 AssistPreviewPanel#0",
- 1080: "\nparent=10\ntype=BufferLayer\nname=WindowToken{9f8a3df android.os.BinderProxy@825027e}#0",
- 1081: "\nparent=1080\ntype=BufferLayer\nname=26d9efb DockedStackDivider#0",
- 1403: "\nparent=10\ntype=BufferLayer\nname=WindowToken{dedcfff android.os.Binder@a80cb1e}#0",
- 1447: "\nparent=39\ntype=BufferLayer\nname=39ca531 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 1454: "\nparent=27\ntype=BufferLayer\nname=NavigationBar#0",
- 1502: "\nparent=10\ntype=BufferLayer\nname=WindowToken{3ea357b android.os.Binder@6d9a90a}#0",
- 1505: "\nparent=1518\ntype=BufferLayer\nname=Task=623#0",
- 1506: "\nparent=1505\ntype=BufferLayer\nname=AppWindowToken{6deed44 token=Token{45cae57 ActivityRecord{7f14bd6 u0 com.android.server.wm.flicker.testapp/.SimpleActivity t623}}}#0",
- 1518: "\nparent=5\ntype=BufferLayer\nname=Stack=51#0",
- 1519: "\nparent=1518\ntype=ColorLayer\nname=animation background stackId=51#0",
- 1521: "\nparent=1506\ntype=BufferLayer\nname=496d52e com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
- 1534: "\nparent=1447\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 1535: "\nparent=39\ntype=BufferLayer\nname=e280197 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 1536: "\nparent=1535\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
-};
-const expectedInvalidLayerVisibility = {
- chips: [],
- proto: invalidLayerVisibilityProto,
- hwcBlob: "",
- isVisible: true,
- kind: "entry",
- rects: [],
- shortName: "2d22h13m17s233ms",
- timestampMs: "252797233543024",
- where: "",
- name: "2d22h13m17s233ms",
- stableId: "LayerTraceEntry",
- visibleLayers: [],
-};
-
-const expectedOrphanLayersLayer = {
- backgroundBlurRadius: 0,
- chips: [],
- cornerRadius: 0,
- effectiveScalingMode: 0,
- hwcCompositionType: "INVALID",
- id: 1012,
- isOpaque: true,
- isRelativeOf: false,
- kind: "1012",
- name: "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
- shadowRadius: 0,
- shortName: "SurfaceView - com.android.(...).Main#0",
- type: "BufferLayer",
- z: -1,
- zOrderRelativeOf: null,
- parentId: 1011,
- stableId: "BufferLayer 1012 SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
- activeBuffer: new ActiveBuffer(1440, 2614, 1472, 1),
- bufferTransform: standardTransform,
- color: standardColor,
- crop: standardCrop,
- hwcFrame: standardRect,
- screenBounds: new RectF(0, 98, 1440, 2712),
- transform: new Transform(0, new Matrix33(1, 0, 0, 0, 1, 98)),
- visibleRegion: new Region([new Rect(0, 98, 1440, 2712)]),
-};
-const expectedOrphanLayersProto = {
- 2: "\nparent=0\ntype=BufferLayer\nname=Display Root#0",
- 3: "\nparent=0\ntype=BufferLayer\nname=Display Overlays#0",
- 4: "\nparent=2\ntype=BufferLayer\nname=mBelowAppWindowsContainers#0",
- 5: "\nparent=2\ntype=BufferLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@e7dd520#0",
- 6: "\nparent=5\ntype=BufferLayer\nname=animationLayer#0",
- 7: "\nparent=5\ntype=BufferLayer\nname=splitScreenDividerAnchor#0",
- 8: "\nparent=2\ntype=BufferLayer\nname=mAboveAppWindowsContainers#0",
- 9: "\nparent=2\ntype=BufferLayer\nname=mImeWindowsContainers#0",
- 10: "\nparent=5\ntype=BufferLayer\nname=Stack=0#0",
- 11: "\nparent=10\ntype=ColorLayer\nname=animation background stackId=0#0",
- 12: "\nparent=9\ntype=BufferLayer\nname=WindowToken{1350b6f android.os.Binder@d1b0e4e}#0",
- 13: "\nparent=4\ntype=BufferLayer\nname=WallpaperWindowToken{4537182 token=android.os.Binder@d87c4cd}#0",
- 18: "\nparent=13\ntype=BufferLayer\nname=8d26107 com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
- 19: "\nparent=18\ntype=BufferLayer\nname=com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
- 20: "\nparent=8\ntype=BufferLayer\nname=WindowToken{fba948d android.os.BinderProxy@756b124}#0",
- 21: "\nparent=20\ntype=BufferLayer\nname=dc26642 DockedStackDivider#0",
- 22: "\nparent=8\ntype=BufferLayer\nname=WindowToken{45663b4 android.os.BinderProxy@5273887}#0",
- 23: "\nparent=22\ntype=BufferLayer\nname=c617bdd AssistPreviewPanel#0",
- 24: "\nparent=8\ntype=BufferLayer\nname=WindowToken{ef90888 android.os.BinderProxy@9d4dc2b}#0",
- 25: "\nparent=24\ntype=BufferLayer\nname=1d24221 NavigationBar#0",
- 26: "\nparent=8\ntype=BufferLayer\nname=WindowToken{6b1dca9 android.os.BinderProxy@a53d830}#0",
- 27: "\nparent=26\ntype=BufferLayer\nname=eaca22e StatusBar#0",
- 28: "\nparent=8\ntype=BufferLayer\nname=WindowToken{72e584c android.os.BinderProxy@3ba407f}#0",
- 29: "\nparent=28\ntype=BufferLayer\nname=46af095 ScreenDecorOverlay#0",
- 30: "\nparent=8\ntype=BufferLayer\nname=WindowToken{bc659b android.os.BinderProxy@f1405aa}#0",
- 31: "\nparent=30\ntype=BufferLayer\nname=80ead38 ScreenDecorOverlayBottom#0",
- 33: "\nparent=27\ntype=BufferLayer\nname=StatusBar#0",
- 34: "\nparent=29\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
- 35: "\nparent=31\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
- 36: "\nparent=10\ntype=BufferLayer\nname=Task=2#0",
- 37: "\nparent=36\ntype=BufferLayer\nname=AppWindowToken{5162f77 token=Token{ac99d76 ActivityRecord{7749795 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t2}}}#0",
- 38: "\nparent=37\ntype=BufferLayer\nname=2c19e73 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 41: "\nparent=25\ntype=BufferLayer\nname=NavigationBar#1",
- 43: "\nparent=37\ntype=BufferLayer\nname=2f0c80b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 46: "\nparent=5\ntype=BufferLayer\nname=Stack=1#0",
- 47: "\nparent=46\ntype=ColorLayer\nname=animation background stackId=1#0",
- 48: "\nparent=46\ntype=BufferLayer\nname=Task=89#0",
- 49: "\nparent=48\ntype=BufferLayer\nname=AppWindowToken{1d514da token=Token{d36fe85 ActivityRecord{e0ec0ef u0 com.android.chrome/com.google.android.apps.chrome.Main t89}}}#0",
- 54: "\nparent=49\ntype=BufferLayer\nname=8ae6e06 com.android.chrome/com.google.android.apps.chrome.Main#0",
- 607: "\nparent=5\ntype=BufferLayer\nname=Stack=9#0",
- 608: "\nparent=607\ntype=ColorLayer\nname=animation background stackId=9#0",
- 609: "\nparent=607\ntype=BufferLayer\nname=Task=97#0",
- 615: "\nparent=609\ntype=BufferLayer\nname=AppWindowToken{28730c9 token=Token{4d768d0 ActivityRecord{faf093 u0 com.google.android.gm/.welcome.WelcomeTourActivity t97}}}#0",
- 616: "\nparent=615\ntype=BufferLayer\nname=44e6e5c com.google.android.gm/com.google.android.gm.welcome.WelcomeTourActivity#0",
- 679: "\nparent=12\ntype=BufferLayer\nname=2d0b1e4 InputMethod#0",
- 993: "\nparent=8\ntype=BufferLayer\nname=WindowToken{e425e58 android.os.Binder@6d9a73b}#0",
- 1011: "\nparent=54\ntype=BufferLayer\nname=com.android.chrome/com.google.android.apps.chrome.Main#0",
- 1012: "\nparent=1011\ntype=BufferLayer\nname=SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
- 1013: "\nparent=1011\ntype=ColorLayer\nname=Background for -SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
-};
-const expectedOrphanLayers = {
- chips: [],
- proto: expectedOrphanLayersProto,
- hwcBlob: "",
- isVisible: true,
- kind: "entry",
- rects: [],
- shortName: "3d23h30m9s820ms",
- timestampMs: "343809820196384",
- where: "",
- name: "3d23h30m9s820ms",
- stableId: "LayerTraceEntry",
- visibleLayers: [],
-};
-
-const expectedRootLayer = {
- backgroundBlurRadius: 0,
- cornerRadius: 0,
- effectiveScalingMode: 0,
- hwcCompositionType: "INVALID",
- id: 12545,
- isOpaque: true,
- isRelativeOf: false,
- kind: "12545",
- name: "com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
- shadowRadius: 0,
- shortName: "com.android.(...).SimpleActivity#0",
- type: "BufferQueueLayer",
- z: 0,
- zOrderRelativeOf: null,
- parentId: 12541,
- stableId: "BufferQueueLayer 12545 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
- activeBuffer: new ActiveBuffer(1440, 2960, 1472, 1),
- chips: [VISIBLE_CHIP],
- bufferTransform: standardTransform,
- color: standardColor,
- crop: new Rect(0, 0, 1440, 2960),
- hwcFrame: standardRect,
- screenBounds: new RectF(0, 0, 1440, 2960),
- sourceBounds: new RectF(0, 0, 1440, 2960),
- transform: standardTransform,
- visibleRegion: new Region([new Rect(0, 0, 1440, 2960)]),
-};
-const expectedRootProto = {
- 2: "\nparent=-1\ntype=ContainerLayer\nname=Root#0",
- 3: "\nparent=2\ntype=ContainerLayer\nname=mWindowContainers#0",
- 4: "\nparent=2\ntype=ContainerLayer\nname=mOverlayContainers#0",
- 5: "\nparent=3\ntype=ContainerLayer\nname=mBelowAppWindowsContainers#0",
- 6: "\nparent=3\ntype=ContainerLayer\nname=com.android.server.wm.DisplayContent$TaskContainers@708b672#0",
- 7: "\nparent=6\ntype=ContainerLayer\nname=animationLayer#0",
- 8: "\nparent=6\ntype=ContainerLayer\nname=boostedAnimationLayer#0",
- 9: "\nparent=6\ntype=ContainerLayer\nname=homeAnimationLayer#0",
- 10: "\nparent=6\ntype=ContainerLayer\nname=splitScreenDividerAnchor#0",
- 11: "\nparent=3\ntype=ContainerLayer\nname=mAboveAppWindowsContainers#0",
- 12: "\nparent=3\ntype=ContainerLayer\nname=ImeContainer#0",
- 13: "\nparent=6\ntype=ContainerLayer\nname=Task=1#0",
- 18: "\nparent=5\ntype=ContainerLayer\nname=WallpaperWindowToken{4c3f8ef token=android.os.Binder@a0341ce}#0",
- 19: "\nparent=18\ntype=ContainerLayer\nname=aa9ba7e com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2#0",
- 20: "\nparent=19\ntype=BufferQueueLayer\nname=com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2#0",
- 23: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{2e98b86 android.os.BinderProxy@6e5dbc8}#0",
- 24: "\nparent=23\ntype=ContainerLayer\nname=5976c47 NavigationBar0#0",
- 25: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{525aa4 android.os.BinderProxy@df1e236}#0",
- 26: "\nparent=25\ntype=ContainerLayer\nname=986c00d NotificationShade#0",
- 27: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{7ec5009 android.os.BinderProxy@de2add3}#0",
- 28: "\nparent=27\ntype=ContainerLayer\nname=3a0542f StatusBar#0",
- 31: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{eef604c android.os.BinderProxy@d3a687f}#0",
- 32: "\nparent=31\ntype=ContainerLayer\nname=20b5895 ScreenDecorOverlay#0",
- 33: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{4846f6f android.os.BinderProxy@39824e}#0",
- 34: "\nparent=33\ntype=ContainerLayer\nname=1d714 ScreenDecorOverlayBottom#0",
- 36: "\nparent=32\ntype=BufferQueueLayer\nname=ScreenDecorOverlay#0",
- 38: "\nparent=34\ntype=BufferQueueLayer\nname=ScreenDecorOverlayBottom#0",
- 40: "\nparent=28\ntype=BufferQueueLayer\nname=StatusBar#0",
- 43: "\nparent=12\ntype=ContainerLayer\nname=WindowToken{fa12db9 android.os.Binder@4b88380}#0",
- 46: "\nparent=13\ntype=ContainerLayer\nname=Task=4#0",
- 47: "\nparent=46\ntype=ContainerLayer\nname=ActivityRecord{99bbfb0 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity#0",
- 54: "\nparent=24\ntype=BufferQueueLayer\nname=NavigationBar0#0",
- 71: "\nparent=43\ntype=ContainerLayer\nname=e8f94d2 InputMethod#0",
- 11499: "\nparent=47\ntype=ContainerLayer\nname=6737b79 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 11501: "\nparent=-1\ntype=ContainerLayer\nname=Input Consumer recents_animation_input_consumer#2",
- 11759: "\nparent=6\ntype=ContainerLayer\nname=Task=873#0",
- 11760: "\nparent=11759\ntype=ContainerLayer\nname=Task=874#0",
- 11761: "\nparent=11760\ntype=ContainerLayer\nname=ActivityRecord{7398002 u0 com.android.server.wm.flicker.testapp/.ImeActivityAutoFocus#0",
- 11785: "\nparent=11761\ntype=ColorLayer\nname=Letterbox - right#0",
- 12131: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{bbffcfd android.os.Binder@547b554}#0",
- 12379: "\nparent=47\ntype=ContainerLayer\nname=3f8f098 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
- 12412: "\nparent=11761\ntype=ContainerLayer\nname=edca7c6 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.ImeActivityAutoFocus#0",
- 12448: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{99bbfb0 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity)/@0x2c3972c - animation-leash#0",
- 12449: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{fc16c94 u0 com.android.server.wm.flicker.testapp/.ImeActivity)/@0x7049863 - animation-leash#0",
- 12485: "\nparent=6\ntype=ContainerLayer\nname=Task=908#0",
- 12486: "\nparent=12485\ntype=ContainerLayer\nname=Task=909#0",
- 12487: "\nparent=12486\ntype=ContainerLayer\nname=ActivityRecord{4b3c5cb u0 com.android.server.wm.flicker.testapp/.ImeActivity#0",
- 12500: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{99bbfb0 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity)/@0x2c3972c - animation-leash#0",
- 12501: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{4b3c5cb u0 com.android.server.wm.flicker.testapp/.ImeActivity)/@0x4ad47a1 - animation-leash#0",
- 12502: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=WallpaperWindowToken{4c3f8ef token=android.os.Binder@a0341ce})/@0xcde5e65 - animation-leash#0",
- 12511: "\nparent=12487\ntype=ColorLayer\nname=Letterbox - right#1",
- 12514: "\nparent=12487\ntype=ContainerLayer\nname=debe1ed com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.ImeActivity#0",
- 12526: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{6b7d663 android.os.BinderProxy@391f21d}#0",
- 12527: "\nparent=12526\ntype=ContainerLayer\nname=32aa260 AssistPreviewPanel#0",
- 12529: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{31f7489 android.os.BinderProxy@67b1e53}#0",
- 12530: "\nparent=12529\ntype=ContainerLayer\nname=cbb28bc DockedStackDivider#0",
- 12536: "\nparent=6\ntype=ContainerLayer\nname=Task=910#0",
- 12537: "\nparent=12536\ntype=ContainerLayer\nname=Task=911#0",
- 12538: "\nparent=12537\ntype=ContainerLayer\nname=ActivityRecord{d3b8a44 u0 com.android.server.wm.flicker.testapp/.SimpleActivity#0",
- 12541: "\nparent=12538\ntype=ContainerLayer\nname=a3583c5 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
- 12545: "\nparent=12541\ntype=BufferQueueLayer\nname=com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
- 2147483645: "\nparent=-1\ntype=\nname=Offscreen Root",
-};
-const expectedRoot = {
- chips: [],
- proto: expectedRootProto,
- hwcBlob: "",
- isVisible: true,
- kind: "entry",
- shortName: "0d1h46m19s146ms",
- timestampMs: "6379146308030",
- where: "",
- name: "0d1h46m19s146ms",
- stableId: "LayerTraceEntry",
-};
-
-const expectedRootAospLayer = {
- backgroundBlurRadius: 0,
- cornerRadius: 0,
- effectiveScalingMode: 0,
- hwcCompositionType: "INVALID",
- id: 876,
- isOpaque: false,
- isRelativeOf: false,
- kind: "876",
- name: "com.android.launcher3/com.android.launcher3.Launcher#0",
- shadowRadius: 0,
- shortName: "com.android.(...).Launcher#0",
- type: "BufferLayer",
- z: 0,
- zOrderRelativeOf: null,
- parentId: 41,
- activeBuffer: new ActiveBuffer(1440, 2880, 1472, 1),
- bufferTransform: standardTransform,
- chips: [VISIBLE_CHIP],
- color: standardColor,
- crop: new Rect(0, 0, 1440, 2880),
- hwcFrame: standardRect,
- screenBounds: new RectF(0, 0, 1440, 2880),
- sourceBounds: new RectF(0, 0, 1440, 2880),
- transform: standardTransform,
- visibleRegion: new Region([new Rect(0, 0, 1440, 2880)]),
-};
-const expectedRootAospProto = {
- 2: "\nparent=-1\ntype=ContainerLayer\nname=Display Root#0",
- 3: "\nparent=-1\ntype=ContainerLayer\nname=Display Overlays#0",
- 4: "\nparent=2\ntype=ContainerLayer\nname=mBelowAppWindowsContainers#0",
- 5: "\nparent=2\ntype=ContainerLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@d8077b3#0",
- 6: "\nparent=5\ntype=ContainerLayer\nname=animationLayer#0",
- 7: "\nparent=5\ntype=ContainerLayer\nname=boostedAnimationLayer#0",
- 8: "\nparent=5\ntype=ContainerLayer\nname=homeAnimationLayer#0",
- 9: "\nparent=5\ntype=ContainerLayer\nname=splitScreenDividerAnchor#0",
- 10: "\nparent=2\ntype=ContainerLayer\nname=mAboveAppWindowsContainers#0",
- 11: "\nparent=2\ntype=ContainerLayer\nname=mImeWindowsContainers#0",
- 12: "\nparent=5\ntype=ContainerLayer\nname=Stack=0#0",
- 13: "\nparent=12\ntype=ColorLayer\nname=animation background stackId=0#0",
- 18: "\nparent=4\ntype=ContainerLayer\nname=WallpaperWindowToken{5a7eaca token=android.os.Binder@438b635}#0",
- 23: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{d19e48 android.os.BinderProxy@560ac3a}#0",
- 24: "\nparent=23\ntype=ContainerLayer\nname=b2a84e1 NavigationBar0#0",
- 25: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{74d6851 android.os.BinderProxy@8b22adb}#0",
- 26: "\nparent=25\ntype=ContainerLayer\nname=16448b6 StatusBar#0",
- 27: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{624863c android.os.BinderProxy@975b02f}#0",
- 28: "\nparent=27\ntype=ContainerLayer\nname=cdb9fc5 ScreenDecorOverlay#0",
- 29: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{cb7204b android.os.BinderProxy@b8f3d1a}#0",
- 30: "\nparent=29\ntype=ContainerLayer\nname=ad1ca28 ScreenDecorOverlayBottom#0",
- 31: "\nparent=28\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
- 32: "\nparent=30\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
- 33: "\nparent=18\ntype=ContainerLayer\nname=4f4b23b com.android.systemui.ImageWallpaper#0",
- 34: "\nparent=33\ntype=BufferLayer\nname=com.android.systemui.ImageWallpaper#0",
- 36: "\nparent=26\ntype=BufferLayer\nname=StatusBar#0",
- 37: "\nparent=12\ntype=ContainerLayer\nname=Task=144#0",
- 38: "\nparent=37\ntype=ContainerLayer\nname=AppWindowToken{54e2de0 token=Token{f4c5fe3 ActivityRecord{6a9dc12 u0 com.android.launcher3/.Launcher t144}}}#0",
- 40: "\nparent=-1\ntype=ContainerLayer\nname=Input Consumer recents_animation_input_consumer#1",
- 41: "\nparent=38\ntype=ContainerLayer\nname=418b5c0 com.android.launcher3/com.android.launcher3.Launcher#0",
- 45: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{9158878 android.os.Binder@4f4a5db}#0",
- 46: "\nparent=24\ntype=BufferLayer\nname=NavigationBar0#0",
- 731: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{c0ebbde android.os.BinderProxy@1af0e60}#0",
- 732: "\nparent=731\ntype=ContainerLayer\nname=b37d1bf AssistPreviewPanel#0",
- 733: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{dc6b7ea android.os.BinderProxy@166b08c}#0",
- 734: "\nparent=733\ntype=ContainerLayer\nname=2a1cadb DockedStackDivider#0",
- 862: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{f63efe6 android.os.Binder@d536e41}#0",
- 865: "\nparent=887\ntype=ContainerLayer\nname=Task=170#0",
- 866: "\nparent=865\ntype=ContainerLayer\nname=AppWindowToken{c829d40 token=Token{59970c3 ActivityRecord{36f2472 u0 com.android.server.wm.flicker.testapp/.PipActivity t170}}}#0",
- 871: "\nparent=866\ntype=ContainerLayer\nname=8153ff7 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.PipActivity#0",
- 876: "\nparent=41\ntype=BufferLayer\nname=com.android.launcher3/com.android.launcher3.Launcher#0",
- 887: "\nparent=5\ntype=ContainerLayer\nname=Stack=78#0",
- 888: "\nparent=887\ntype=ColorLayer\nname=animation background stackId=78#0",
-};
-const expectedRootAosp = {
- chips: [],
- proto: expectedRootAospProto,
- hwcBlob: "",
- isVisible: true,
- kind: "entry",
- shortName: "0d1h3m1s911ms",
- timestampMs: "3781911657318",
- where: "",
- name: "0d1h3m1s911ms",
- stableId: "LayerTraceEntry",
-};
-
-const expectedEntries = [
- expectedEmptyRegion,
- expectedInvalidLayerVisibility,
- expectedOrphanLayers,
- expectedRoot,
- expectedRootAosp
-];
-const expectedLayers = [
- expectedEmptyRegionLayer,
- expectedInvalidLayerVisibilityLayer,
- expectedOrphanLayersLayer,
- expectedRootLayer,
- expectedRootAospLayer
-];
-const layers_traces = [
- '../spec/traces/layers_trace/layers_trace_emptyregion.pb',
- '../spec/traces/layers_trace/layers_trace_invalid_layer_visibility.pb',
- '../spec/traces/layers_trace/layers_trace_orphanlayers.pb',
- '../spec/traces/layers_trace/layers_trace_root.pb',
- '../spec/traces/layers_trace/layers_trace_root_aosp.pb',
-];
-
-export { expectedEntries, expectedLayers, layers_traces };
diff --git a/tools/winscope/spec/traces/error_trace.winscope b/tools/winscope/spec/traces/error_trace.winscope
deleted file mode 100644
index fd97072..0000000
--- a/tools/winscope/spec/traces/error_trace.winscope
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/spec/traces/layers_trace/layers_trace_emptyregion.pb b/tools/winscope/spec/traces/layers_trace/layers_trace_emptyregion.pb
deleted file mode 100644
index 98ee6f3..0000000
--- a/tools/winscope/spec/traces/layers_trace/layers_trace_emptyregion.pb
+++ /dev/null
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
deleted file mode 100644
index 20572d7..0000000
--- a/tools/winscope/spec/traces/layers_trace/layers_trace_invalid_layer_visibility.pb
+++ /dev/null
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
deleted file mode 100644
index af40797..0000000
--- a/tools/winscope/spec/traces/layers_trace/layers_trace_orphanlayers.pb
+++ /dev/null
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
deleted file mode 100644
index d961714..0000000
--- a/tools/winscope/spec/traces/layers_trace/layers_trace_root.pb
+++ /dev/null
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
deleted file mode 100644
index 666b328..0000000
--- a/tools/winscope/spec/traces/layers_trace/layers_trace_root_aosp.pb
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/spec/traces/regular_rotation_in_last_state_layers_trace.winscope b/tools/winscope/spec/traces/regular_rotation_in_last_state_layers_trace.winscope
deleted file mode 100644
index 36fc663..0000000
--- a/tools/winscope/spec/traces/regular_rotation_in_last_state_layers_trace.winscope
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/spec/traces/regular_rotation_in_last_state_wm_trace.winscope b/tools/winscope/spec/traces/regular_rotation_in_last_state_wm_trace.winscope
deleted file mode 100644
index b5537ac..0000000
--- a/tools/winscope/spec/traces/regular_rotation_in_last_state_wm_trace.winscope
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/spec/traces/tag_trace.winscope b/tools/winscope/spec/traces/tag_trace.winscope
deleted file mode 100644
index 83459ba..0000000
--- a/tools/winscope/spec/traces/tag_trace.winscope
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/spec/traces/wl_trace.pb b/tools/winscope/spec/traces/wl_trace.pb
deleted file mode 100644
index 7e1f007..0000000
--- a/tools/winscope/spec/traces/wl_trace.pb
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/spec/utils/tree.js b/tools/winscope/spec/utils/tree.js
deleted file mode 100644
index 57e0ea7..0000000
--- a/tools/winscope/spec/utils/tree.js
+++ /dev/null
@@ -1,58 +0,0 @@
-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 };
- this.name = undefined;
- this.stableId = undefined;
- this.kind = undefined;
- this.shortName = undefined;
- }
-}
-
-class ObjNode extends Node {
- constructor(name, children, combined, stableId) {
- const nodeDef = {
- kind: '',
- name: name,
- stableId: stableId,
- };
- if (combined) {
- nodeDef.combined = true;
- }
- super(nodeDef, children);
- }
-}
-
-class ObjDiffNode extends ObjNode {
- constructor(name, diffType, children, combined, stableId) {
- super(name, children, combined, stableId);
- 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, ObjNode, ObjDiffNode, toPlainObject };
\ No newline at end of file
diff --git a/tools/winscope/src/AccessibilityTraceView.vue b/tools/winscope/src/AccessibilityTraceView.vue
deleted file mode 100644
index 5c91bb4..0000000
--- a/tools/winscope/src/AccessibilityTraceView.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-<!-- 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"
- :presentTags="[]"
- :presentErrors="[]"
- />
-</template>
-
-<script>
-import TraceView from "@/TraceView.vue"
-
-export default {
- name: "AccessibilityTraceView",
- props: ["store", "file"],
- components: {
- TraceView
- },
- methods: {
- summarizer(item) {
- return null;
- },
- }
-}
-</script>
\ No newline at end of file
diff --git a/tools/winscope/src/App.vue b/tools/winscope/src/App.vue
deleted file mode 100644
index 6341bcc..0000000
--- a/tools/winscope/src/App.vue
+++ /dev/null
@@ -1,500 +0,0 @@
-<!-- Copyright (C) 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.
--->
-<template>
- <div id="app">
- <vue-title :appName="appName" :traceName="traceNameForTitle" />
-
- <md-dialog-prompt
- class="edit-trace-name-dialog"
- :md-active.sync="editingTraceName"
- v-model="traceName"
- md-title="Edit trace name"
- md-input-placeholder="Enter a new trace name"
- md-confirm-text="Save" />
-
- <md-app>
- <md-app-toolbar md-tag="md-toolbar" class="top-toolbar">
- <h1 class="md-title">{{appName}}</h1>
-
- <div class="trace-name" v-if="dataLoaded">
- <div>
- <span>{{ traceName }}</span>
- <!-- <input type="text" class="trace-name-editable" v-model="traceName" /> -->
- <md-icon class="edit-trace-name-btn" @click.native="editTraceName()">edit</md-icon>
- </div>
- </div>
-
- <md-button
- class="md-primary md-theme-default download-all-btn"
- @click="generateTags()"
- v-if="dataLoaded && canGenerateTags"
- >Generate Tags</md-button>
- <md-button
- class="md-primary md-theme-default"
- @click="downloadAsZip(files, traceName)"
- 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" />
- </div>
- <div class="input" @dragover.prevent @drop.prevent>
- <datainput class="fileinput" ref="input" :store="store"
- @dataReady="onDataReady" />
- </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"
- :presentTags="Object.freeze(presentTags)"
- :presentErrors="Object.freeze(presentErrors)"
- :dataViewFiles="dataViewFiles"
- @click="onDataViewFocus(file)"
- />
- </div>
-
- <overlay
- :presentTags="Object.freeze(presentTags)"
- :presentErrors="Object.freeze(presentErrors)"
- :store="store"
- :ref="overlayRef"
- :searchTypes="searchTypes"
- v-if="dataLoaded"
- v-on:bottom-nav-height-change="handleBottomNavHeightChange"
- />
- </section>
- </md-app-content>
- </md-app>
- </div>
-</template>
-<script>
-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 Searchbar from './Searchbar.vue';
-import {NAVIGATION_STYLE, SEARCH_TYPE} from './utils/consts';
-import {TRACE_TYPES, FILE_TYPES, dataFile} from './decode.js';
-import { TaggingEngine } from './flickerlib/common';
-import titleComponent from './Title.vue';
-
-const APP_NAME = 'Winscope';
-
-const CONTENT_BOTTOM_PADDING = 25;
-
-export default {
- name: 'app',
- mixins: [FileType, SaveAsZip, FocusedDataViewFinder],
- data() {
- return {
- appName: APP_NAME,
- activeDataView: null,
- // eslint-disable-next-line new-cap
- store: LocalStore('app', {
- flattened: false,
- onlyVisible: false,
- simplifyNames: true,
- displayDefaults: true,
- navigationStyle: NAVIGATION_STYLE.GLOBAL,
- flickerTraceView: false,
- showFileTypes: [],
- isInputMode: false,
- }),
- overlayRef: 'overlay',
- mainContentStyle: {
- 'padding-bottom': `${CONTENT_BOTTOM_PADDING}px`,
- },
- tagFile: null,
- presentTags: [],
- presentErrors: [],
- searchTypes: [SEARCH_TYPE.TIMESTAMP],
- hasTagOrErrorTraces: false,
- traceName: "unnamed_winscope_trace",
- editingTraceName: false
- };
- },
- created() {
- window.addEventListener('keydown', this.onKeyDown);
- window.addEventListener('scroll', this.onScroll);
- // document.title = this.traceName;
- },
- destroyed() {
- window.removeEventListener('keydown', this.onKeyDown);
- window.removeEventListener('scroll', this.onScroll);
- },
-
- methods: {
- /** Get states from either tag files or error files */
- getUpdatedStates(files) {
- var states = [];
- for (const file of files) {
- states.push(...file.data);
- }
- return states;
- },
- /** Get tags from all uploaded tag files*/
- getUpdatedTags() {
- if (this.tagFile === null) return [];
- const tagStates = this.getUpdatedStates([this.tagFile]);
- var tags = [];
- tagStates.forEach(tagState => {
- tagState.tags.forEach(tag => {
- tag.timestamp = Number(tagState.timestamp);
- // tags generated on frontend have transition.name due to kotlin enum
- tag.transition = tag.transition.name ?? tag.transition;
- tags.push(tag);
- });
- });
- return tags;
- },
- /** Get tags from all uploaded error files*/
- getUpdatedErrors() {
- var errorStates = this.getUpdatedStates(this.errorFiles);
- var errors = [];
- //TODO (b/196201487) add check if errors empty
- errorStates.forEach(errorState => {
- errorState.errors.forEach(error => {
- error.timestamp = Number(errorState.timestamp);
- errors.push(error);
- });
- });
- return errors;
- },
- /** Set flicker mode check for if there are tag/error traces uploaded*/
- updateHasTagOrErrorTraces() {
- return this.hasTagTrace() || this.hasErrorTrace();
- },
- hasTagTrace() {
- return this.tagFile !== null;
- },
- hasErrorTrace() {
- return this.errorFiles.length > 0;
- },
- /** Activate flicker search tab if tags/errors uploaded*/
- updateSearchTypes() {
- this.searchTypes = [SEARCH_TYPE.TIMESTAMP];
- if (this.hasTagTrace()) {
- this.searchTypes.push(SEARCH_TYPE.TRANSITIONS);
- }
- if (this.hasErrorTrace()) {
- this.searchTypes.push(SEARCH_TYPE.ERRORS);
- }
- },
- /** Filter data view files by current show settings*/
- updateShowFileTypes() {
- this.store.showFileTypes = this.dataViewFiles
- .filter((file) => file.show)
- .map(file => file.type);
- },
- clear() {
- this.store.showFileTypes = [];
- this.tagFile = null;
- this.$store.commit('clearFiles');
- this.recordButtonClickedEvent("Clear")
- },
- onDataViewFocus(file) {
- this.$store.commit('setActiveFile', file);
- this.activeDataView = file.type;
- },
- onKeyDown(event) {
- event = event || window.event;
- if (this.store.isInputMode) return false;
- if (event.keyCode == 37 /* left */ ) {
- this.$store.dispatch('advanceTimeline', DIRECTION.BACKWARD);
- } else if (event.keyCode == 39 /* right */ ) {
- this.$store.dispatch('advanceTimeline', DIRECTION.FORWARD);
- } else if (event.keyCode == 38 /* up */ ) {
- this.$refs[this.activeView][0].arrowUp();
- } else if (event.keyCode == 40 /* down */ ) {
- this.$refs[this.activeView][0].arrowDown();
- } else {
- return false;
- }
- event.preventDefault();
- return true;
- },
- onDataReady(traceName, files) {
- this.traceName = traceName;
- this.$store.dispatch('setFiles', files);
-
- this.tagFile = this.tagFiles[0] ?? null;
- this.hasTagOrErrorTraces = this.updateHasTagOrErrorTraces();
- this.presentTags = this.getUpdatedTags();
- this.presentErrors = this.getUpdatedErrors();
- this.updateSearchTypes();
- this.updateFocusedView();
- this.updateShowFileTypes();
- },
- setStatus(status) {
- if (status) {
- this.title = status;
- } else {
- this.title = APP_NAME;
- }
- },
- handleBottomNavHeightChange(newHeight) {
- this.$set(
- this.mainContentStyle,
- 'padding-bottom',
- `${ CONTENT_BOTTOM_PADDING + newHeight }px`,
- );
- },
- generateTags() {
- // generate tag file
- this.recordButtonClickedEvent("Generate Tags");
- const engine = new TaggingEngine(
- this.$store.getters.tagGenerationWmTrace,
- this.$store.getters.tagGenerationSfTrace,
- (text) => { console.log(text) }
- );
- const tagTrace = engine.run();
- const tagFile = this.generateTagFile(tagTrace);
-
- // update tag trace in set files, update flicker mode
- this.tagFile = tagFile;
- this.hasTagOrErrorTraces = this.updateHasTagOrErrorTraces();
- this.presentTags = this.getUpdatedTags();
- this.presentErrors = this.getUpdatedErrors();
- this.updateSearchTypes();
- },
-
- generateTagFile(tagTrace) {
- const data = tagTrace.entries;
- const blobUrl = URL.createObjectURL(new Blob([], {type: undefined}));
- return dataFile(
- "GeneratedTagTrace.winscope",
- data.map((x) => x.timestamp),
- data,
- blobUrl,
- FILE_TYPES.TAG_TRACE
- );
- },
-
- editTraceName() {
- this.editingTraceName = true;
- }
- },
- computed: {
- files() {
- return this.$store.getters.sortedFiles.map(file => {
- if (this.hasDataView(file)) {
- file.show = true;
- }
- return file;
- });
- },
- prettyDump() {
- return JSON.stringify(this.dump, null, 2);
- },
- dataLoaded() {
- return this.files.length > 0;
- },
- activeView() {
- if (!this.activeDataView && this.files.length > 0) {
- // 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((file) => this.hasDataView(file));
- },
- tagFiles() {
- return this.$store.getters.tagFiles;
- },
- errorFiles() {
- return this.$store.getters.errorFiles;
- },
- timelineFiles() {
- return this.$store.getters.timelineFiles;
- },
- canGenerateTags() {
- const fileTypes = this.dataViewFiles.map((file) => file.type);
- return fileTypes.includes(TRACE_TYPES.WINDOW_MANAGER)
- && fileTypes.includes(TRACE_TYPES.SURFACE_FLINGER);
- },
- traceNameForTitle() {
- if (!this.dataLoaded) {
- return undefined;
- } else {
- return this.traceName;
- }
- }
- },
- watch: {
- // title() {
- // document.title = this.title;
- // },
- },
- components: {
- overlay: Overlay,
- dataview: DataView,
- datainput: DataInput,
- dataadb: DataAdb,
- searchbar: Searchbar,
- ["vue-title"]: titleComponent,
- },
-};
-</script>
-<style>
-@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap');
-
-#app .md-app-container {
- /* Get rid of transforms 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 {
- border-bottom: 1px solid rgba(0, 0, 0, .12);
-}
-
-.timeline {
- margin: 16px;
-}
-
-.container {
- display: flex;
- flex-wrap: wrap;
-}
-
-.md-button {
- margin-top: 1em
-}
-
-h1 {
- font-weight: normal;
-}
-
-.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;
-}
-
-.snackbar-break-words {
- /* These are technically the same, but use both */
- overflow-wrap: break-word;
- word-wrap: break-word;
- -ms-word-break: break-all;
- word-break: break-word;
- /* Adds a hyphen where the word breaks, if supported (No Blink) */
- -ms-hyphens: auto;
- -moz-hyphens: auto;
- -webkit-hyphens: auto;
- hyphens: auto;
- padding: 10px 10px 10px 10px;
-}
-
-.trace-name {
- flex: 1;
- display: flex;
- align-items: center;
- align-content: center;
- justify-content: center;
- font-family: 'Open Sans', sans-serif;
- font-size: 1rem;
-}
-
-.md-icon.edit-trace-name-btn {
- color: rgba(0, 0, 0, 0.6)!important;
- font-size: 1rem!important;
- margin-bottom: 0.1rem;
-}
-
-.md-icon.edit-trace-name-btn:hover {
- cursor: pointer;
-}
-
-.trace-name-editable {
- all: unset;
- cursor: default;
-}
-
-.edit-trace-name-dialog .md-dialog-container {
- min-width: 350px;
-}
-
-.md-overlay.md-dialog-overlay {
- z-index: 10;
-}
-</style>
diff --git a/tools/winscope/src/DataAdb.vue b/tools/winscope/src/DataAdb.vue
deleted file mode 100644
index bb653d9..0000000
--- a/tools/winscope/src/DataAdb.vue
+++ /dev/null
@@ -1,449 +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>
- <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-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>
- <span class="md-subheading">Unable to connect to Winscope ADB proxy</span>
- <div class="md-body-2">
- <p>Launch the Winscope ADB Connect proxy to capture traces directly from your browser.</p>
- <p>Python 3.5+ and ADB is required.</p>
- <p>Run:</p>
- <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-button class="md-accent" :href="downloadProxyUrl" @click="buttonClicked(`Download from AOSP`)">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">
- <md-icon class="md-accent">update</md-icon>
- <span class="md-subheading">The version of Winscope ADB Connect proxy running on your machine is incopatibile with Winscope.</span>
- <div class="md-body-2">
- <p>Please update the proxy to version {{ proxyClient.VERSION }}</p>
- <p>Run:</p>
- <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-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-field>
- <label>Enter Winscope proxy token</label>
- <md-input v-model="proxyClient.store.proxyKey"></md-input>
- </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-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(proxyClient.devices).length > 0 ? "Connected devices:" : "No devices detected" }}</div>
- <md-list>
- <md-list-item v-for="(device, id) in proxyClient.devices" :key="id" @click="proxyClient.selectDevice(id)" :disabled="!device.authorised">
- <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-progress-spinner :md-size="30" md-indeterminate></md-progress-spinner>
- </md-card-content>
- <md-card-content v-if="status === STATES.START_TRACE">
- <div class="device-choice">
- <md-list>
- <md-list-item>
- <md-icon>smartphone</md-icon>
- <span class="md-list-item-text">{{ proxyClient.devices[proxyClient.selectedDevice].model }} ({{ proxyClient.selectedDevice }})</span>
- </md-list-item>
- </md-list>
- <md-button class="md-primary" @click="resetLastDevice">Change device</md-button>
- </div>
- <div class="trace-section">
- <h3>Trace targets:</h3>
- <div class="selection">
- <md-checkbox class="md-primary" v-for="traceKey in Object.keys(DYNAMIC_TRACES)" :key="traceKey" v-model="traceStore[traceKey]">{{ DYNAMIC_TRACES[traceKey].name }}</md-checkbox>
- </div>
- <div class="trace-config">
- <h4>Surface Flinger config</h4>
- <div class="selection">
- <md-checkbox class="md-primary" v-for="config in TRACE_CONFIG['layers_trace']" :key="config" v-model="traceStore[config]">{{config}}</md-checkbox>
- <div class="selection">
- <md-field class="config-selection" v-for="selectConfig in Object.keys(SF_SELECTED_CONFIG)" :key="selectConfig">
- <md-select v-model="SF_SELECTED_CONFIG_VALUES[selectConfig]" :placeholder="selectConfig">
- <md-option value="">{{selectConfig}}</md-option>
- <md-option v-for="option in SF_SELECTED_CONFIG[selectConfig]" :key="option" :value="option">{{ option }}</md-option>
- </md-select>
- </md-field>
- </div>
- </div>
- </div>
- <div class="trace-config">
- <h4>Window Manager config</h4>
- <div class="selection">
- <md-field class="config-selection" v-for="selectConfig in Object.keys(WM_SELECTED_CONFIG)" :key="selectConfig">
- <md-select v-model="WM_SELECTED_CONFIG_VALUES[selectConfig]" :placeholder="selectConfig">
- <md-option value="">{{selectConfig}}</md-option>
- <md-option v-for="option in WM_SELECTED_CONFIG[selectConfig]" :key="option" :value="option">{{ option }}</md-option>
- </md-select>
- </md-field>
- </div>
- </div>
- <md-button class="md-primary trace-btn" @click="startTrace">Start trace</md-button>
- </div>
- <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="traceStore[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">
- <md-icon class="md-accent">error</md-icon>
- <span class="md-subheading">Error:</span>
- <pre>
- {{ errorText }}
- </pre>
- <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-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-bar md-mode="determinate" :md-value="loadProgress"></md-progress-bar>
- </md-card-content>
- </flat-card>
-</template>
-<script>
-import LocalStore from './localstore.js';
-import FlatCard from './components/FlatCard.vue';
-import {proxyClient, ProxyState, ProxyEndpoint} from './proxyclient/ProxyClient.ts';
-
-// trace options should be added in a nested category
-const TRACES = {
- 'default': {
- 'window_trace': {
- name: 'Window Manager',
- },
- 'accessibility_trace': {
- name: 'Accessibility',
- },
- 'layers_trace': {
- name: 'Surface Flinger',
- },
- 'transactions': {
- name: 'Transaction',
- },
- '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',
- },
- },
- 'arc': {
- 'wayland_trace': {
- name: 'Wayland',
- },
- },
-};
-
-const TRACE_CONFIG = {
- 'layers_trace': [
- 'composition',
- 'metadata',
- 'hwc',
- 'tracebuffers',
- ],
-};
-
-const SF_SELECTED_CONFIG = {
- 'sfbuffersize': [
- '4000',
- '8000',
- '16000',
- '32000',
- ],
-};
-
-const WM_SELECTED_CONFIG = {
- 'wmbuffersize': [
- '4000',
- '8000',
- '16000',
- '32000',
- ],
- 'tracingtype': [
- 'frame',
- 'transaction',
- ],
- 'tracinglevel': [
- 'verbose',
- 'debug',
- 'critical',
- ],
-};
-
-const DUMPS = {
- 'window_dump': {
- name: 'Window Manager',
- },
- 'layers_dump': {
- name: 'Surface Flinger',
- },
-};
-
-const CONFIGS = Object.keys(TRACE_CONFIG).flatMap((file) => TRACE_CONFIG[file]);
-
-export default {
- name: 'dataadb',
- data() {
- return {
- proxyClient,
- ProxyState,
- STATES: ProxyState,
- TRACES,
- DYNAMIC_TRACES: TRACES['default'],
- TRACE_CONFIG,
- SF_SELECTED_CONFIG,
- WM_SELECTED_CONFIG,
- SF_SELECTED_CONFIG_VALUES: {},
- WM_SELECTED_CONFIG_VALUES: {},
- DUMPS,
- status: ProxyState.CONNECTING,
- dataFiles: [],
- keep_alive_worker: null,
- errorText: '',
- loadProgress: 0,
- traceStore: LocalStore(
- 'trace',
- Object.assign(
- this.getAllTraceKeys(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',
- onStateChangeFn: (newState, errorText) => {
- this.status = newState;
- this.errorText = errorText;
- },
- };
- },
- props: ['store'],
- components: {
- 'flat-card': FlatCard,
- },
- methods: {
- getAllTraceKeys(traces) {
- let keys = [];
- for (let dict_key in traces) {
- for (let key in traces[dict_key]) {
- keys.push(key);
- }
- }
- return keys;
- },
- setAvailableTraces() {
- this.DYNAMIC_TRACES = this.TRACES['default'];
- proxyClient.call('GET', ProxyEndpoint.CHECK_WAYLAND, this, function(request, view) {
- try {
- if(request.responseText == 'true') {
- view.appendOptionalTraces('arc');
- }
- } catch(err) {
- console.error(err);
- proxyClient.setState(ProxyState.ERROR, request.responseText);
- }
- });
- },
- appendOptionalTraces(device_key) {
- for(let key in this.TRACES[device_key]) {
- this.$set(this.DYNAMIC_TRACES, key, this.TRACES[device_key][key]);
- }
- },
- keepAliveTrace() {
- if (this.status !== ProxyState.END_TRACE) {
- clearInterval(this.keep_alive_worker);
- this.keep_alive_worker = null;
- return;
- }
- proxyClient.call('GET', `${ProxyEndpoint.STATUS}${proxyClient.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);
- }
- });
- },
- startTrace() {
- const requested = this.toTrace();
- const requestedConfig = this.toTraceConfig();
- const requestedSelectedSfConfig = this.toSelectedSfTraceConfig();
- const requestedSelectedWmConfig = this.toSelectedWmTraceConfig();
- if (requested.length < 1) {
- proxyClient.setState(ProxyState.ERROR, 'No targets selected');
- this.recordNewEvent("No targets selected");
- return;
- }
-
- this.recordNewEvent("Start Trace");
- proxyClient.call('POST', `${ProxyEndpoint.CONFIG_TRACE}${proxyClient.deviceId()}/`, this, null, null, requestedConfig);
- proxyClient.call('POST', `${ProxyEndpoint.SELECTED_SF_CONFIG_TRACE}${proxyClient.deviceId()}/`, this, null, null, requestedSelectedSfConfig);
- proxyClient.call('POST', `${ProxyEndpoint.SELECTED_WM_CONFIG_TRACE}${proxyClient.deviceId()}/`, this, null, null, requestedSelectedWmConfig);
- proxyClient.setState(ProxyState.END_TRACE);
- proxyClient.call('POST', `${ProxyEndpoint.START_TRACE}${proxyClient.deviceId()}/`, this, function(request, view) {
- view.keepAliveTrace();
- }, null, requested);
- },
- dumpState() {
- this.recordButtonClickedEvent("Dump State");
- const requested = this.toDump();
- if (requested.length < 1) {
- proxyClient.setState(ProxyState.ERROR, 'No targets selected');
- this.recordNewEvent("No targets selected");
- return;
- }
- proxyClient.setState(ProxyState.LOAD_DATA);
- proxyClient.call('POST', `${ProxyEndpoint.DUMP}${proxyClient.deviceId()}/`, this, function(request, view) {
- proxyClient.loadFile(requested, 0, "dump", view);
- }, null, requested);
- },
- endTrace() {
- proxyClient.setState(ProxyState.LOAD_DATA);
- proxyClient.call('POST', `${ProxyEndpoint.END_TRACE}${proxyClient.deviceId()}/`, this, function(request, view) {
- proxyClient.loadFile(view.toTrace(), 0, "trace", view);
- });
- this.recordNewEvent("Ended Trace");
- },
- toTrace() {
- return Object.keys(this.DYNAMIC_TRACES)
- .filter((traceKey) => this.traceStore[traceKey]);
- },
- toTraceConfig() {
- return Object.keys(TRACE_CONFIG)
- .filter((file) => this.traceStore[file])
- .flatMap((file) => TRACE_CONFIG[file])
- .filter((config) => this.traceStore[config]);
- },
- toSelectedSfTraceConfig() {
- const requestedSelectedConfig = {};
- for (const config in this.SF_SELECTED_CONFIG_VALUES) {
- if (this.SF_SELECTED_CONFIG_VALUES[config] !== "") {
- requestedSelectedConfig[config] = this.SF_SELECTED_CONFIG_VALUES[config];
- }
- }
- return requestedSelectedConfig;
- },
- toSelectedWmTraceConfig() {
- const requestedSelectedConfig = {};
- for (const config in this.WM_SELECTED_CONFIG_VALUES) {
- if (this.WM_SELECTED_CONFIG_VALUES[config] !== "") {
- requestedSelectedConfig[config] = this.WM_SELECTED_CONFIG_VALUES[config];
- }
- }
- return requestedSelectedConfig;
- },
- toDump() {
- return Object.keys(DUMPS)
- .filter((dumpKey) => this.traceStore[dumpKey]);
- },
- restart() {
- this.recordButtonClickedEvent("Connect / Retry");
- proxyClient.setState(ProxyState.CONNECTING);
- },
- resetLastDevice() {
- this.recordButtonClickedEvent("Change Device");
- this.proxyClient.resetLastDevice();
- this.restart();
- },
- },
- created() {
- proxyClient.setState(ProxyState.CONNECTING);
- this.proxyClient.onStateChange(this.onStateChangeFn);
- const urlParams = new URLSearchParams(window.location.search);
- if (urlParams.has('token')) {
- this.proxyClient.proxyKey = urlParams.get('token');
- }
- this.proxyClient.getDevices();
- },
- beforeDestroy() {
- this.proxyClient.removeOnStateChange(this.onStateChangeFn);
- },
- watch: {
- status: {
- handler(st) {
- if (st == ProxyState.CONNECTING) {
- this.proxyClient.getDevices();
- }
- if (st == ProxyState.START_TRACE) {
- this.setAvailableTraces();
- }
- },
- },
- },
-};
-
-</script>
-<style scoped>
-.config-selection {
- width: 150px;
- display: inline-flex;
- margin-left: 5px;
- margin-right: 5px;
-}
-.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/DataInput.vue b/tools/winscope/src/DataInput.vue
deleted file mode 100644
index c4f1cf0..0000000
--- a/tools/winscope/src/DataInput.vue
+++ /dev/null
@@ -1,698 +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 @dragleave="fileDragOut" @dragover="fileDragIn" @drop="handleFileDrop">
- <flat-card style="min-width: 50em">
- <md-card-header>
- <div class="md-title">Open files</div>
- </md-card-header>
- <md-card-content>
- <div class="dropbox" @click="$refs.fileUpload.click()" ref="dropbox">
- <md-list
- class="uploaded-files"
- v-show="Object.keys(dataFiles).length > 0"
- >
- <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="e => {
- e.stopPropagation()
- onRemoveFile(file.type)
- }"
- >
- <md-icon>close</md-icon>
- </md-button>
- </md-list-item>
- </md-list>
- <div class="progress-spinner-wrapper" v-show="loadingFiles">
- <md-progress-spinner
- :md-diameter="30"
- :md-stroke="3"
- md-mode="indeterminate"
- class="progress-spinner"
- />
- </div>
- <input
- type="file"
- @change="onLoadFile"
- v-on:drop="handleFileDrop"
- ref="fileUpload"
- id="dropzone"
- v-show="false"
- multiple
- />
- <p v-if="!dataReady && !loadingFiles">
- Drag your <b>.winscope</b> or <b>.zip</b> file(s) or click here to begin
- </p>
- </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>
- <div class="md-layout">
- <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
- >
- <p class="snackbar-break-words">{{ snackbarText }}</p>
- <div @click="hideSnackbarMessage()">
- <md-button class="md-icon-button">
- <md-icon style="color: white">close</md-icon>
- </md-button>
- </div>
- </md-snackbar>
- </flat-card>
-</div>
-</template>
-<script>
-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,
- FILE_DECODERS,
- FILE_ICONS,
- fileType: 'auto',
- dataFiles: {},
- loadingFiles: false,
- showFetchingSnackbar: false,
- showSnackbar: false,
- snackbarDuration: 3500,
- snackbarText: '',
- fetchingSnackbarText: 'Fetching files...',
- traceName: undefined,
- };
- },
- props: ['store'],
- created() {
- // Attempt to load files from extension if present
- this.loadFilesFromExtension();
- },
- mounted() {
- this.handleDropboxDragEvents();
- },
- beforeUnmount() {
-
- },
- methods: {
- showSnackbarMessage(message, duration) {
- this.snackbarText = '\n' + message + '\n';
- this.snackbarDuration = duration;
- this.showSnackbar = true;
- },
- hideSnackbarMessage() {
- this.showSnackbar = false;
- this.recordButtonClickedEvent("Hide Snackbar Message")
- },
- 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);
- },
- });
- },
- handleDropboxDragEvents() {
- // Counter used to keep track of when we actually exit the dropbox area
- // When we drag over a child of the dropbox area the dragenter event will
- // be called again and subsequently the dragleave so we don't want to just
- // remove the class on the dragleave event.
- let dropboxDragCounter = 0;
-
- console.log(this.$refs["dropbox"])
-
- this.$refs["dropbox"].addEventListener('dragenter', e => {
- dropboxDragCounter++;
- this.$refs["dropbox"].classList.add('dragover');
- });
-
- this.$refs["dropbox"].addEventListener('dragleave', e => {
- dropboxDragCounter--;
- if (dropboxDragCounter == 0) {
- this.$refs["dropbox"].classList.remove('dragover');
- }
- });
-
- this.$refs["dropbox"].addEventListener('drop', e => {
- dropboxDragCounter = 0;
- this.$refs["dropbox"].classList.remove('dragover');
- });
- },
- /**
- * 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);
- }
- });
- }
- },
- fileDragIn(e) {
- e.preventDefault();
- },
- fileDragOut(e) {
- e.preventDefault();
- },
- handleFileDrop(e) {
- e.preventDefault();
- let droppedFiles = e.dataTransfer.files;
- if(!droppedFiles) return;
- // Record analytics event
- this.recordDragAndDropFileEvent(droppedFiles);
-
- this.processFiles(droppedFiles);
- },
- onLoadFile(e) {
- const files = event.target.files || event.dataTransfer.files;
- this.recordFileUploadEvent(files);
- this.processFiles(files);
- },
- async processFiles(files) {
- console.log("Object.keys(this.dataFiles).length", Object.keys(this.dataFiles).length)
- // The trace name to use if we manage to load the archive without errors.
- let tmpTraceName;
-
- if (Object.keys(this.dataFiles).length > 0) {
- // We have already loaded some files so only want to use the name of
- // this archive as the name of the trace if we override all loaded files
- } else {
- // No files have been uploaded yet so if we are uploading only 1 archive
- // we want to use it's name as the trace name
- if (files.length == 1 && this.isArchive(files[0])) {
- tmpTraceName = this.getFileNameWithoutZipExtension(files[0])
- }
- }
-
- 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;
- }
-
- // 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);
-
- const frozenData = Object.freeze(decodedFile.data.data);
- delete decodedFile.data.data;
- decodedFile.data.data = frozenData;
-
- this.$set(this.dataFiles,
- dataType, Object.freeze(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);
- if (selectedFile.data) {
- selectedFile.data = Object.freeze(selectedFile.data);
- }
-
- this.$set(this.dataFiles, dataType, Object.freeze(selectedFile));
-
- // Remove selected file from overriden list
- const index = files.indexOf(selectedFile);
- files.splice(index, 1);
- }
- }
-
- if (overriddenFileTypes.size > 0) {
- this.displayFilesOverridenWarning(overriddenFiles);
- }
-
- if (tmpTraceName !== undefined) {
- this.traceName = tmpTraceName;
- }
- },
-
- getFileNameWithoutZipExtension(file) {
- const fileNameSplitOnDot = file.name.split('.')
- if (fileNameSplitOnDot.slice(-1)[0] == 'zip') {
- return fileNameSplitOnDot.slice(0,-1).join('.');
- } else {
- return file.name;
- }
- },
-
- /**
- * 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.`);
- }
-
- 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'));
- }
- },
-
- getFileExtensions(file) {
- const split = file.name.split('.');
- if (split.length > 1) {
- return split.pop();
- }
-
- return undefined;
- },
-
- isArchive(file) {
- 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.
- return type === 'bugreport' ||
- (type === 'auto' && (extension === 'zip' ||
- file.type === 'application/zip'))
- },
-
- async addFile(file) {
- const decodedFiles = [];
-
- if (this.isArchive(file)) {
- 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};
- },
- /**
- * Decode a zip file
- *
- * Load all files that can be decoded, even if some failures occur.
- * For example, a zip file with an mp4 recorded via MediaProjection
- * doesn't include the winscope metadata (b/140855415), but the trace
- * files within the zip should be nevertheless readable
- */
- async decodeArchive(archive) {
- const buffer = await this.readFile(archive);
-
- const zip = new JSZip();
- const content = await zip.loadAsync(buffer);
-
- const decodedFiles = [];
-
- let lastError;
- for (const filename in content.files) {
- 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)) {
- lastError = e;
- }
-
- console.error(e);
- }
- }
-
- if (decodedFiles.length == 0) {
- if (lastError) {
- throw lastError;
- }
- throw new Error('No matching files found in archive', archive);
- } else {
- if (lastError) {
- this.showSnackbarMessage(
- 'Unable to parse all files, check log for more details', 3500);
- }
- }
-
- return decodedFiles;
- },
- onRemoveFile(typeName) {
- this.$delete(this.dataFiles, typeName);
- },
- onSubmit() {
- this.$emit('dataReady', this.formattedTraceName,
- Object.keys(this.dataFiles).map((key) => this.dataFiles[key]));
- },
- },
- computed: {
- dataReady: function() {
- return Object.keys(this.dataFiles).length > 0;
- },
-
- formattedTraceName() {
- if (this.traceName === undefined) {
- return 'winscope-trace';
- } else {
- return this.traceName;
- }
- }
- },
- components: {
- 'flat-card': FlatCard,
- },
-};
-
-</script>
-<style>
- .dropbox:hover, .dropbox.dragover {
- background: rgb(224, 224, 224);
- }
-
- .dropbox {
- outline: 2px dashed #448aff; /* the dash box */
- outline-offset: -10px;
- background: white;
- color: #448aff;
- padding: 10px 10px 10px 10px;
- min-height: 200px; /* minimum height */
- position: relative;
- cursor: pointer;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-items: center;
- }
-
- .dropbox p, .dropbox .progress-spinner-wrapper {
- font-size: 1.2em;
- margin: auto;
- }
-
- .progress-spinner-wrapper, .progress-spinner {
- width: fit-content;
- height: fit-content;
- display: block;
- }
-
- .progress-spinner-wrapper {
- padding: 1.5rem 0 1.5rem 0;
- }
-
- .dropbox .uploaded-files {
- background: none!important;
- width: 100%;
- }
-</style>
diff --git a/tools/winscope/src/DataView.vue b/tools/winscope/src/DataView.vue
deleted file mode 100644
index d185f40..0000000
--- a/tools/winscope/src/DataView.vue
+++ /dev/null
@@ -1,213 +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 @click="onClick($event)">
- <flat-card v-if="hasDataView(file)">
- <md-card-header>
- <md-card-header-text>
- <div class="md-title">
- <button class="toggle-view-button" @click="toggleView">
- <i aria-hidden="true" class="md-icon md-theme-default material-icons">
- {{ isShowFileType(file.type) ? "expand_more" : "chevron_right" }}
- </i>
- </button>
- <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>
- <AccessibilityTraceView
- v-if="showInAccessibilityTraceView(file) && isShowFileType(file.type)"
- :store="store"
- :file="file"
- ref="view"
- />
- <WindowManagerTraceView
- v-if="showInWindowManagerTraceView(file) && isShowFileType(file.type)"
- :store="store"
- :file="file"
- :presentTags="presentTags"
- :presentErrors="presentErrors"
- ref="view"
- />
- <SurfaceFlingerTraceView
- v-else-if="showInSurfaceFlingerTraceView(file) && isShowFileType(file.type)"
- :store="store"
- :file="file"
- :presentTags="presentTags"
- :presentErrors="presentErrors"
- ref="view"
- />
- <transactionsviewlegacy
- v-else-if="isTransactionsLegacy(file) && isShowFileType(file.type)"
- :trace="file"
- ref="view"
- />
- <logview
- v-else-if="isLog(file) && isShowFileType(file.type)"
- :file="file"
- ref="view"
- />
- <traceview
- v-else-if="showInTraceView(file) && isShowFileType(file.type)"
- :store="store"
- :file="file"
- :presentTags="[]"
- :presentErrors="[]"
- ref="view"
- />
- <div v-else>
- <h1 v-if="isShowFileType(file.type)" class="bad">Unrecognized DataType</h1>
- </div>
-
- </flat-card>
- </div>
-</template>
-<script>
-import TraceView from '@/TraceView.vue';
-import AccessibilityTraceView from '@/AccessibilityTraceView.vue';
-import WindowManagerTraceView from '@/WindowManagerTraceView.vue';
-import SurfaceFlingerTraceView from '@/SurfaceFlingerTraceView.vue';
-import TransactionsViewLegacy from '@/TransactionsViewLegacy.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 {
- TRACE_ICONS,
- };
- },
- methods: {
- // Recursively search for an arrowUp method in the children
- // This is necessary because the VueComponent hierarchy has
- // different depths depending on the source
- depthFirstSearchArrowUp(component) {
- if (component.arrowUp) {
- component.arrowUp();
- return true;
- } else {
- for (let i = 0; i < component.$children.length; i++) {
- var child = component.$children[i];
- if (this.depthFirstSearchArrowUp(child)) {
- return true;
- }
- }
- return false;
- }
- },
- // Recursively search for an arrowUp method in the children
- // This is necessary because the VueComponent hierarchy has
- // different depths depending on the source
- depthFirstSearchArrowDown(component) {
- if (component.arrowDown) {
- component.arrowDown();
- return true
- } else {
- for (let i = 0; i < component.$children.length; i++) {
- var child = component.$children[i];
- if (this.depthFirstSearchArrowDown(child)) {
- return true;
- }
- }
- return false;
- }
- },
- arrowUp() {
- for (let i = 0; i < this.$children.length; i++) {
- var child = this.$children[i];
- let done = this.depthFirstSearchArrowUp(child);
- if (done) {
- return true;
- }
- }
- return false;
- },
- arrowDown() {
- for (let i = 0; i < this.$children.length; i++) {
- var child = this.$children[i];
- let done = this.depthFirstSearchArrowDown(child);
- if (done) {
- return true;
- }
- }
- return false;
- },
- onClick(e) {
- // Pass click event to parent, so that click event handler can be attached
- // to component.
- this.$emit('click', e);
- },
- /** Filter data view files by current show settings */
- updateShowFileTypes() {
- this.store.showFileTypes = this.dataViewFiles
- .filter((file) => file.show)
- .map(file => file.type);
- },
- /** Expand or collapse data view */
- toggleView() {
- this.file.show = !this.file.show;
- this.updateShowFileTypes();
- },
- /** Check if data view file should be shown */
- isShowFileType(type) {
- return this.store.showFileTypes.find(fileType => fileType===type);
- },
- },
- props: ['store', 'file', 'presentTags', 'presentErrors', 'dataViewFiles'],
- mixins: [FileType],
- components: {
- 'traceview': TraceView,
- 'transactionsviewlegacy': TransactionsViewLegacy,
- 'logview': LogView,
- 'flat-card': FlatCard,
- AccessibilityTraceView,
- WindowManagerTraceView,
- SurfaceFlingerTraceView,
- },
-};
-</script>
-<style>
-.bad {
- margin: 1em 1em 1em 1em;
- font-size: 4em;
- color: red;
-}
-
-.toggle-view-button {
- background: none;
- color: inherit;
- border: none;
- font: inherit;
- cursor: pointer;
- padding-right: 10px;
- display: inline-block;
-}
-
-.md-title {
- display: inline-block;
-}
-</style>
diff --git a/tools/winscope/src/DefaultTreeElement.vue b/tools/winscope/src/DefaultTreeElement.vue
deleted file mode 100644
index ac03f9b..0000000
--- a/tools/winscope/src/DefaultTreeElement.vue
+++ /dev/null
@@ -1,141 +0,0 @@
-<!-- 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>
- <div class="flicker-tags" v-for="transition in transitions" :key="transition">
- <Arrow
- class="transition-arrow"
- :style="{color: transitionArrowColor(transition)}"
- />
- <md-tooltip md-direction="right"> {{transitionTooltip(transition)}} </md-tooltip>
- </div>
- <div class="flicker-tags" v-for="error in errors" :key="error.message">
- <Arrow class="error-arrow"/>
- <md-tooltip md-direction="right"> {{errorTooltip(error.message)}} </md-tooltip>
- </div>
- </span>
-</template>
-
-<script>
-
-import Arrow from './components/TagDisplay/Arrow.vue';
-import {transitionMap} from './utils/consts.js';
-
-export default {
- name: 'DefaultTreeElement',
- props: ['item', 'simplify-names', 'errors', 'transitions'],
- methods: {
- chipClassForChip(c) {
- return [
- 'tree-view-internal-chip',
- 'tree-view-chip',
- 'tree-view-chip' + '-' +
- (c.type?.toString() || c.class?.toString() || 'default'),
- ];
- },
- transitionArrowColor(transition) {
- return transitionMap.get(transition).color;
- },
- transitionTooltip(transition) {
- return transitionMap.get(transition).desc;
- },
- errorTooltip(errorMessage) {
- if (errorMessage.length>100) {
- return `Error: ${errorMessage.substring(0,100)}...`;
- }
- return `Error: ${errorMessage}`;
- },
- },
- components: {
- Arrow,
- },
-};
-</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;
-}
-
-.flicker-tags {
- display: inline-block;
-}
-
-.error-arrow {
- color: red;
-}
-</style>
diff --git a/tools/winscope/src/DraggableDiv.vue b/tools/winscope/src/DraggableDiv.vue
deleted file mode 100644
index adccaf4..0000000
--- a/tools/winscope/src/DraggableDiv.vue
+++ /dev/null
@@ -1,229 +0,0 @@
-<!-- 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
deleted file mode 100644
index 90f41d7..0000000
--- a/tools/winscope/src/LogEntry.vue
+++ /dev/null
@@ -1,188 +0,0 @@
-<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
deleted file mode 100644
index dfec233..0000000
--- a/tools/winscope/src/LogView.vue
+++ /dev/null
@@ -1,300 +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>
- <md-card-content class="container">
- <div class="navigation">
- <md-content
- md-tag="md-toolbar"
- md-elevation="0"
- class="card-toolbar md-transparent md-dense"
- >
- <h2 class="md-title" style="flex: 1">Log View</h2>
- <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>
- </md-content>
- </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" v-bind:key="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" v-bind:key="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;
- // Record analytics event
- this.recordOpenTraceEvent("ProtoLog");
-
- 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,
- 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: {
- arrowUp() {
- this.isSelected = !this.isSelected;
- return !this.isSelected;
- },
- arrowDown() {
- this.isSelected = !this.isSelected;
- return !this.isSelected;
- },
- 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: {
- pinnedToLatest(isPinned) {
- if (isPinned) {
- this.scrollToRow(this.lastOccuredVisibleIndex);
- }
- },
- currentTimestamp: {
- immediate: true,
- handler(newTimestamp) {
- this.prevLastOccuredIndex = this.lastOccuredIndex;
- this.lastOccuredIndex = this.getLastOccuredIndex(this.data, newTimestamp);
-
- if (this.pinnedToLatest) {
- this.scrollToRow(this.lastOccuredVisibleIndex);
- }
- },
- }
- },
- props: ['file'],
- computed: {
- 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>
-.container {
- display: flex;
- flex-wrap: wrap;
-}
-
-.filters, .navigation {
- width: 100%;
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.navigation {
- justify-content: flex-end;
-}
-
-.navigation > button {
- margin: 0;
-}
-
-.filters > div {
- margin: 10px;
-}
-
-.log-header {
- display: inline-flex;
- color: var(--md-theme-default-text-accent-on-background, rgba(0,0,0,0.54));
- font-weight: bold;
-}
-
-.log-header > div {
- padding: 6px 10px;
- border-bottom: 1px solid #f1f1f1;
-}
-
-.log-header .time-column {
- width: 13em;
-}
-
-.log-header .tag-column {
- width: 10em;
-}
-
-.log-header .at-column {
- width: 30em;
-}
-
-.column-title {
- font-size: 12px;
-}
-
-.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
deleted file mode 100644
index 4faa129..0000000
--- a/tools/winscope/src/NodeContextMenu.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<template>
- <vue-context ref="menu">
-
- </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: 10;
- 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
deleted file mode 100644
index df14e33..0000000
--- a/tools/winscope/src/Overlay.vue
+++ /dev/null
@@ -1,955 +0,0 @@
-<!-- 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="">
- <searchbar
- class="search-bar"
- v-if="search"
- :searchTypes="searchTypes"
- :store="store"
- :presentTags="Object.freeze(presentTags)"
- :presentErrors="Object.freeze(presentErrors)"
- :timeline="mergedTimeline.timeline"
- />
- <md-toolbar
- md-elevation="0"
- class="md-transparent">
-
- <md-button
- @click="toggleSearch()"
- class="drop-search"
- >
- Toggle search bar
- </md-button>
-
- <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
- v-if="multipleTraces"
- ref="navigationTypeSelection"
- class="navigation-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"
- >
- <input
- class="timestamp-search-input"
- v-model="searchInput"
- spellcheck="false"
- :placeholder="seekTime"
- @focus="updateInputMode(true)"
- @blur="updateInputMode(false)"
- @keyup.enter="updateSearchForTimestamp"
- />
- <timeline
- :store="store"
- :flickerMode="flickerMode"
- :tags="Object.freeze(presentTags)"
- :errors="Object.freeze(presentErrors)"
- :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" @click="recordButtonClickedEvent(`Expand Timeline`)">Expand timeline</md-tooltip>
- </md-icon>
- <md-icon v-else>
- expand_more
- <md-tooltip md-direction="top" @click="recordButtonClickedEvent(`Collapse Timeline`)">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>
- <input
- class="timestamp-search-input"
- :class="{ expanded: expanded }"
- v-model="searchInput"
- spellcheck="false"
- :placeholder="seekTime"
- @focus="updateInputMode(true)"
- @blur="updateInputMode(false)"
- @keyup.enter="updateSearchForTimestamp"
- />
- </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 Searchbar from './Searchbar.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, getClosestTimestamp} from './transform.js';
-
-export default {
- name: 'overlay',
- props: ['store', 'presentTags', 'presentErrors', 'searchTypes'],
- 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,
- search: false,
- searchInput: "",
- isSeekTimeInputMode: false,
- };
- },
- created() {
- this.mergedTimeline = this.computeMergedTimeline();
- this.$store.commit('setMergedTimeline', this.mergedTimeline);
- this.updateNavigationFileFilter();
- },
- mounted() {
- this.emitBottomHeightUpdate();
- },
- destroyed() {
- this.$store.commit('removeMergedTimeline', this.mergedTimeline);
- this.updateInputMode(false);
- },
- 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) {
- console.warn('Unexpected navigation type; fallback to global');
- return 'All timelines';
- }
-
- 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) {
- console.warn('Unexpected navigation type; fallback to global');
- return 'public';
- }
-
- const fileType = split[1];
-
- return TRACE_ICONS[fileType];
- }
- },
- minimizedTimeline() {
- if (this.navigationStyle === NAVIGATION_STYLE.GLOBAL) {
- return this.mergedTimeline;
- }
-
- if (this.navigationStyle === NAVIGATION_STYLE.FOCUSED) {
- //dumps do not have a timeline, so if scrolling over a dump, show merged timeline
- if (this.focusedFile.timeline) {
- return this.focusedFile;
- }
- return this.mergedTimeline;
- }
-
- if (this.navigationStyle === NAVIGATION_STYLE.CUSTOM) {
- // TODO: Return custom timeline
- return this.mergedTimeline;
- }
-
- if (
- this.navigationStyle.split('-').length >= 2
- && this.navigationStyle.split('-')[0] === NAVIGATION_STYLE.TARGETED
- ) {
- return this.$store.state
- .traces[this.navigationStyle.split('-')[1]];
- }
-
- console.warn('Unexpected navigation type; fallback to global');
- return this.mergedTimeline;
- },
- isCropped() {
- return this.crop != null &&
- (this.crop.left !== 0 || this.crop.right !== 1);
- },
- multipleTraces() {
- return this.timelineFiles.length > 1;
- },
- flickerMode() {
- return this.presentTags.length>0 || this.presentErrors.length>0;
- },
- },
- updated() {
- this.$nextTick(() => {
- if (this.$refs.expandedTimeline && this.expanded) {
- this.videoHeight = this.$refs.expandedTimeline.clientHeight;
- } else {
- this.videoHeight = 'auto';
- }
- });
- },
- methods: {
- toggleSearch() {
- this.search = !(this.search);
- this.recordButtonClickedEvent("Toggle Search Bar");
- },
- /**
- * determines whether left/right arrow keys should move cursor in input field
- * and upon click of input field, fills with current timestamp
- */
- updateInputMode(isInputMode) {
- this.isSeekTimeInputMode = isInputMode;
- this.store.isInputMode = isInputMode;
- if (!isInputMode) {
- this.searchInput = "";
- } else {
- this.searchInput = this.seekTime;
- }
- },
- /** Navigates to closest timestamp in timeline to search input*/
- updateSearchForTimestamp() {
- const closestTimestamp = getClosestTimestamp(this.searchInput, this.mergedTimeline.timeline);
- this.$store.dispatch("updateTimelineTime", closestTimestamp);
- this.updateInputMode(false);
- this.recordNewEvent("Searching for timestamp")
- },
-
- 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);
- }
-
- var timelineToAdvance = 0;
- while (timelineToAdvance !== undefined) {
- timelineToAdvance = undefined;
- let minTime = Infinity;
-
- 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;
- this.recordButtonClickedEvent("Close Video Overlay")
- },
- openVideoOverlay() {
- this.showVideoOverlay = true;
- this.recordButtonClickedEvent("Open Video Overlay")
- },
- toggleVideoOverlay() {
- this.showVideoOverlay = !this.showVideoOverlay;
- this.recordButtonClickedEvent("Toggle Video Overlay")
- },
- 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) {
- console.warn('Unexpected navigation type; fallback to global');
- navigationStyleFilter = (f) => true;
- break;
- }
-
- const fileType = split[1];
- navigationStyleFilter =
- (f) => f.type === fileType;
- }
- this.recordChangedNavigationStyleEvent(this.navigationStyle);
- 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,
- 'searchbar': Searchbar,
- },
-};
-</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;
- z-index: 10;
-}
-
-.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;
- cursor: text;
-}
-
-.minimized-timeline-content .minimized-timeline {
- margin-top: 4px;
-}
-
-.navigation-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;
-}
-
-.timestamp-search-input {
- outline: none;
- border-width: 0 0 1px;
- border-color: gray;
- font-family: inherit;
- color: #448aff;
- font-size: 12px;
- padding: 0;
- letter-spacing: inherit;
- width: 125px;
-}
-
-.timestamp-search-input:focus {
- border-color: #448aff;
-}
-
-.timestamp-search-input.expanded {
- font-size: 14px;
- width: 150px;
-}
-
-.drop-search:hover {
- background-color: #9af39f;
-}
-</style>
diff --git a/tools/winscope/src/PropertiesTreeElement.vue b/tools/winscope/src/PropertiesTreeElement.vue
deleted file mode 100644
index 07293f9..0000000
--- a/tools/winscope/src/PropertiesTreeElement.vue
+++ /dev/null
@@ -1,84 +0,0 @@
-<!-- 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
deleted file mode 100644
index be32087..0000000
--- a/tools/winscope/src/Rects.vue
+++ /dev/null
@@ -1,160 +0,0 @@
-<!-- Copyright (C) 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.
--->
-<template>
- <div class="bounds" :style="boundsStyle">
- <div
- class="rect" v-for="rect in filteredRects"
- :style="rectToStyle(rect)"
- @click="onClick(rect)"
- v-bind:key="`${rect.left}-${rect.right}-${rect.top}-${rect.bottom}-${rect.ref.name}`"
- >
- <span class="label">{{rect.label}}</span>
- </div>
- <div
- class="highlight"
- v-if="highlight"
- :style="rectToStyle(highlight)"
- />
- <div
- class="displayRect" v-for="rect in displayRects"
- :style="rectToStyle(rect)"
- v-bind:key="`${rect.left}-${rect.right}-${rect.top}-${rect.bottom}-${rect.id}`"
- />
- </div>
-</template>
-
-<script>
-
-// eslint-disable-next-line camelcase
-import {multiplyRect} from './matrix_utils.js';
-
-export default {
- name: 'rects',
- props: ['bounds', 'rects', 'highlight','displays'],
- data() {
- return {
- desiredHeight: 800,
- desiredWidth: 400,
- };
- },
- computed: {
- boundsC() {
- if (this.bounds) {
- return this.bounds;
- }
- var width = Math.max(
- ...this.rects.map((rect) => multiplyRect(rect.transform, rect).right));
- var height = Math.max(
- ...this.rects.map((rect) => multiplyRect(rect.transform, rect).bottom));
-
- // constrain max bounds to prevent boundless layers from shrinking visible displays
- if (this.hasDisplays) {
- width = Math.min(width, this.maxWidth);
- height = Math.min(height, this.maxHeight);
- }
- return {width, height};
- },
- maxWidth() {
- return Math.max(...this.displayRects.map(rect => rect.width)) * 1.3;
- },
- maxHeight() {
- return Math.max(...this.displayRects.map(rect => rect.height)) * 1.3;
- },
- hasDisplays() {
- return this.displays.length > 0;
- },
- boundsStyle() {
- return this.rectToStyle({top: 0, left: 0, right: this.boundsC.width,
- bottom: this.boundsC.height});
- },
- filteredRects() {
- return this.rects.filter((rect) => {
- const isVisible = rect.ref.isVisible;
- return isVisible;
- });
- },
- displayRects() {
- return this.displays.map(display => {
- var rect = display.layerStackSpace;
- rect.id = display.id;
- return rect;
- });
- },
- },
- methods: {
- 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(rect) {
- const x = this.s(rect.left);
- const y = this.s(rect.top);
- const w = this.s(rect.right) - this.s(rect.left);
- const h = this.s(rect.bottom) - this.s(rect.top);
-
- let t;
- if (rect.transform && rect.transform.matrix) {
- t = rect.transform.matrix;
- } else {
- t = rect.transform;
- }
-
- const tr = t ? `matrix(${t.dsdx}, ${t.dtdx}, ${t.dsdy}, ${t.dtdy}, ` +
- `${this.s(t.tx)}, ${this.s(t.ty)})` : '';
- const rectStyle = `top: ${y}px; left: ` +
- `${x}px; height: ${h}px; width: ${w}px; ` +
- `transform: ${tr}; transform-origin: 0 0;`;
- return rectStyle;
- },
- onClick(rect) {
- this.$emit('rect-click', rect.ref);
- },
- },
-};
-</script>
-
-<style scoped>
-.bounds {
- position: relative;
- overflow: hidden;
-}
-.highlight, .rect, .displayRect {
- position: absolute;
- box-sizing: border-box;
- display: flex;
- justify-content: flex-end;
-}
-.rect {
- border: 1px solid black;
- background-color: rgba(146, 149, 150, 0.8);
-}
-.highlight {
- border: 2px solid rgb(235, 52, 52);
- background-color: rgba(243, 212, 212, 0.25);
- pointer-events: none;
-}
-.displayRect {
- border: 4px dashed #195aca;
- pointer-events: none;
-}
-.label {
- align-self: center;
-}
-</style>
diff --git a/tools/winscope/src/Searchbar.vue b/tools/winscope/src/Searchbar.vue
deleted file mode 100644
index c1de4e3..0000000
--- a/tools/winscope/src/Searchbar.vue
+++ /dev/null
@@ -1,356 +0,0 @@
-<!-- Copyright (C) 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.
--->
-<template>
- <md-content class="searchbar">
-
- <div class="tabs">
- <div class="search-timestamp" v-if="isTimestampSearch()">
- <md-field md-inline class="search-input">
- <label>Enter timestamp</label>
- <md-input
- v-model="searchInput"
- v-on:focus="updateInputMode(true)"
- v-on:blur="updateInputMode(false)"
- @keyup.enter.native="updateSearchForTimestamp"
- />
- </md-field>
- <md-button
- class="md-dense md-primary search-timestamp-button"
- @click="updateSearchForTimestamp"
- >
- Go to timestamp
- </md-button>
- </div>
-
- <div class="dropdown-content" v-if="isTransitionSearch()">
- <table>
- <tr class="header">
- <th style="width: 10%">Global Start</th>
- <th style="width: 10%">Global End</th>
- <th style="width: 80%">Transition</th>
- </tr>
-
- <tr v-for="item in filteredTransitionsAndErrors" :key="item.id">
- <td
- v-if="isTransition(item)"
- class="inline-time"
- @click="
- setCurrentTimestamp(transitionStart(transitionTags(item.id)))
- "
- >
- <span>{{ transitionTags(item.id)[0].desc }}</span>
- </td>
- <td
- v-if="isTransition(item)"
- class="inline-time"
- @click="setCurrentTimestamp(transitionEnd(transitionTags(item.id)))"
- >
- <span>{{ transitionTags(item.id)[1].desc }}</span>
- </td>
- <td
- v-if="isTransition(item)"
- class="inline-transition"
- :style="{color: transitionTextColor(item.transition)}"
- @click="setCurrentTimestamp(transitionStart(transitionTags(item.id)))"
- >
- {{ transitionDesc(item.transition) }}
- </td>
- </tr>
- </table>
- <md-field md-inline class="search-input">
- <label>
- Filter by transition name. Click to navigate to closest
- timestamp in active timeline.
- </label>
- <md-input
- v-model="searchInput"
- v-on:focus="updateInputMode(true)"
- v-on:blur="updateInputMode(false)"
- />
- </md-field>
- </div>
-
- <div class="dropdown-content" v-if="isErrorSearch()">
- <table>
- <tr class="header">
- <th style="width: 10%">Timestamp</th>
- <th style="width: 90%">Error Message</th>
- </tr>
-
- <tr v-for="item in filteredTransitionsAndErrors" :key="item.id">
- <td
- v-if="!isTransition(item)"
- class="inline-time"
- @click="setCurrentTimestamp(item.timestamp)"
- >
- {{ errorDesc(item.timestamp) }}
- </td>
- <td
- v-if="!isTransition(item)"
- class="inline-error"
- @click="setCurrentTimestamp(item.timestamp)"
- >
- {{ `${item.assertionName} ${item.message}` }}
- </td>
- </tr>
- </table>
- <md-field md-inline class="search-input">
- <label>
- Filter by error message. Click to navigate to closest
- timestamp in active timeline.
- </label>
- <md-input
- v-model="searchInput"
- v-on:focus="updateInputMode(true)"
- v-on:blur="updateInputMode(false)"
- />
- </md-field>
- </div>
- </div>
-
- <div class="tab-container" v-if="searchTypes.length > 0">
- Search mode:
- <md-button
- v-for="searchType in searchTypes"
- :key="searchType"
- @click="setSearchType(searchType)"
- :class="tabClass(searchType)"
- >
- {{ searchType }}
- </md-button>
- </div>
- </md-content>
-</template>
-<script>
-import { transitionMap, SEARCH_TYPE } from "./utils/consts";
-import { nanos_to_string, getClosestTimestamp } from "./transform";
-
-export default {
- name: "searchbar",
- props: ["store", "presentTags", "timeline", "presentErrors", "searchTypes"],
- data() {
- return {
- searchType: SEARCH_TYPE.TIMESTAMP,
- searchInput: "",
- };
- },
- methods: {
- /** Set search type depending on tab selected */
- setSearchType(searchType) {
- this.searchType = searchType;
- },
- /** Set tab class to determine color highlight for active tab */
- tabClass(searchType) {
- var isActive = (this.searchType === searchType) ? 'active' : 'inactive';
- return ['tab', isActive];
- },
-
- /** Filter all the tags present in the trace by the searchbar input */
- filteredTags() {
- var tags = [];
- var filter = this.searchInput.toUpperCase();
- this.presentTags.forEach((tag) => {
- const tagTransition = tag.transition.toUpperCase();
- if (tagTransition.includes(filter)) tags.push(tag);
- });
- return tags;
- },
- /** Add filtered errors to filtered tags to integrate both into table*/
- filteredTagsAndErrors() {
- var tagsAndErrors = [...this.filteredTags()];
- var filter = this.searchInput.toUpperCase();
- this.presentErrors.forEach((error) => {
- const errorMessage = error.message.toUpperCase();
- if (errorMessage.includes(filter)) tagsAndErrors.push(error);
- });
- // sort into chronological order
- tagsAndErrors.sort((a, b) => (a.timestamp > b.timestamp ? 1 : -1));
-
- return tagsAndErrors;
- },
- /** Each transition has two tags present
- * Isolate the tags for the desire transition
- * Add a desc to display the timestamps as strings
- */
- transitionTags(id) {
- var tags = this.filteredTags().filter((tag) => tag.id === id);
- tags.forEach((tag) => {
- tag.desc = nanos_to_string(tag.timestamp);
- });
- return tags;
- },
-
- /** Find the start as minimum timestamp in transition tags */
- transitionStart(tags) {
- var times = tags.map((tag) => tag.timestamp);
- return times[0];
- },
- /** Find the end as maximum timestamp in transition tags */
- transitionEnd(tags) {
- var times = tags.map((tag) => tag.timestamp);
- return times[times.length - 1];
- },
- /**
- * Upon selecting a start/end tag in the dropdown;
- * navigates to that timestamp in the timeline
- */
- setCurrentTimestamp(timestamp) {
- this.$store.dispatch("updateTimelineTime", timestamp);
- },
-
- /** Colour codes text of transition in dropdown */
- transitionTextColor(transition) {
- return transitionMap.get(transition).color;
- },
- /** Displays transition description rather than variable name */
- transitionDesc(transition) {
- return transitionMap.get(transition).desc;
- },
- /** Add a desc to display the error timestamps as strings */
- errorDesc(timestamp) {
- return nanos_to_string(timestamp);
- },
-
- /** Navigates to closest timestamp in timeline to search input*/
- updateSearchForTimestamp() {
- const closestTimestamp = getClosestTimestamp(this.searchInput, this.timeline);
- this.setCurrentTimestamp(closestTimestamp);
- },
-
- isTransitionSearch() {
- return this.searchType === SEARCH_TYPE.TRANSITIONS;
- },
- isErrorSearch() {
- return this.searchType === SEARCH_TYPE.ERRORS;
- },
- isTimestampSearch() {
- return this.searchType === SEARCH_TYPE.TIMESTAMP;
- },
- isTransition(item) {
- return item.stacktrace === undefined;
- },
-
- /** determines whether left/right arrow keys should move cursor in input field */
- updateInputMode(isInputMode) {
- this.store.isInputMode = isInputMode;
- },
- },
- computed: {
- filteredTransitionsAndErrors() {
- var ids = [];
- return this.filteredTagsAndErrors().filter((item) => {
- if (this.isTransition(item) && !ids.includes(item.id)) {
- item.transitionStart = true;
- ids.push(item.id);
- }
- return !this.isTransition(item) || this.isTransition(item) && item.transitionStart;
- });
- },
- },
- destroyed() {
- this.updateInputMode(false);
- },
-};
-</script>
-<style scoped>
-.searchbar {
- background-color: rgb(250, 243, 233) !important;
- top: 0;
- left: 0;
- right: 0;
- width: 100%;
- margin-left: auto;
- margin-right: auto;
- bottom: 1px;
-}
-
-.tabs {
- padding-top: 1rem;
-}
-
-.tab-container {
- padding-left: 20px;
- display: flex;
- align-items: center;
-}
-
-.tab.active {
- background-color: rgb(236, 222, 202);
-}
-
-.tab.inactive {
- background-color: rgb(250, 243, 233);
-}
-
-.search-timestamp {
- padding: 5px 20px 0px 20px;
- display: inline-flex;
- width: 100%;
-}
-
-.search-timestamp > .search-input {
- margin-top: -5px;
- max-width: 200px;
-}
-
-.search-timestamp-button {
- left: 0;
- padding: 0 15px;
-}
-
-.dropdown-content {
- padding: 5px 20px 0px 20px;
- display: block;
-}
-
-.dropdown-content table {
- overflow-y: scroll;
- max-height: 150px;
- display: block;
-}
-
-.dropdown-content table td {
- padding: 5px;
-}
-
-.dropdown-content table th {
- text-align: left;
- padding: 5px;
-}
-
-.inline-time:hover {
- background: rgb(216, 250, 218);
- cursor: pointer;
-}
-
-.inline-transition {
- font-weight: bold;
-}
-
-.inline-transition:hover {
- background: rgb(216, 250, 218);
- cursor: pointer;
-}
-
-.inline-error {
- font-weight: bold;
- color: red;
-}
-
-.inline-error:hover {
- background: rgb(216, 250, 218);
- cursor: pointer;
-}
-</style>
diff --git a/tools/winscope/src/SurfaceFlingerTraceView.vue b/tools/winscope/src/SurfaceFlingerTraceView.vue
deleted file mode 100644
index 165afb1..0000000
--- a/tools/winscope/src/SurfaceFlingerTraceView.vue
+++ /dev/null
@@ -1,69 +0,0 @@
-<!-- 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"
- :presentTags="presentTags"
- :presentErrors="presentErrors"
- />
-</template>
-
-<script>
-import TraceView from '@/TraceView.vue';
-
-export default {
- name: 'SurfaceFlingerTraceView',
- props: ['store', 'file', 'presentTags', 'presentErrors'],
- components: {
- TraceView,
- },
- methods: {
- summarizer(layer) {
- const summary = [];
-
- if (layer?.visibilityReason) {
- let reason = "";
- if (Array.isArray(layer.visibilityReason)) {
- reason = layer.visibilityReason.join(", ");
- } else {
- reason = layer.visibilityReason;
- }
-
- summary.push({key: 'Invisible due to', value: reason});
- }
-
- if (layer?.occludedBy?.length > 0) {
- summary.push({key: 'Occluded by', value: layer.occludedBy.map(it => it.id).join(', ')});
- }
-
- if (layer?.partiallyOccludedBy?.length > 0) {
- summary.push({
- key: 'Partially occluded by',
- value: layer.partiallyOccludedBy.map(it => it.id).join(', '),
- });
- }
-
- if (layer?.coveredBy?.length > 0) {
- summary.push({key: 'Covered by', value: layer.coveredBy.map(it => it.id).join(', ')});
- }
-
- return summary;
- },
- },
-};
-</script>
diff --git a/tools/winscope/src/Timeline.vue b/tools/winscope/src/Timeline.vue
deleted file mode 100644
index 5b373d2..0000000
--- a/tools/winscope/src/Timeline.vue
+++ /dev/null
@@ -1,146 +0,0 @@
-<!-- Copyright (C) 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.
--->
-<template>
- <div class="timeline-container">
- <div class="tag-timeline" v-if="flickerMode" :style="maxOverlap">
- <transition-container
- class="container"
- v-for="transition in timelineTransitions"
- :key="transition.type"
- :startPos="transition.startPos"
- :startTime="transition.startTime"
- :endTime="transition.endTime"
- :width="transition.width"
- :color="transition.color"
- :overlap="transition.overlap"
- :tooltip="transition.tooltip"
- :store="store"
- />
- </div>
- <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"
- />
- <line
- v-for="error in errorPositions"
- :key="error.pos"
- :x1="`${error.pos}%`"
- :x2="`${error.pos}%`"
- y1="0"
- y2="18px"
- class="error"
- @click="onErrorClick(error.ts)"
- />
- </svg>
- </div>
-</template>
-<script>
-import TimelineMixin from "./mixins/Timeline.js";
-import TransitionContainer from './components/TagDisplay/TransitionContainer.vue';
-
-export default {
- name: "timeline",
- // TODO: Add indication of trim, at least for collasped timeline
- components: {
- 'transition-container': TransitionContainer,
- },
- props: ["selectedIndex", "crop", "disabled", "store"],
- data() {
- return {
- pointHeight: 15,
- corner: 2
- };
- },
- mixins: [TimelineMixin],
- computed: {
- timestamps() {
- if (this.timeline.length == 1) {
- return [0];
- }
- return this.timeline;
- },
- selected() {
- return this.timeline[this.selectedIndex];
- },
- maxOverlap() {
- if (!this.timelineTransitions) {
- return {
- marginTop: '0px',
- }
- }
- var overlaps = [];
- for (const transition in this.timelineTransitions) {
- overlaps.push(this.timelineTransitions[transition].overlap);
- }
- return {
- marginTop: (Math.max(...overlaps)+1)*10 + 'px',
- }
- },
- }
-};
-</script>
-<style scoped>
-.timeline-container {
- width: 100%;
-}
-.container:hover {
- cursor: pointer;
-}
-.tag-timeline {
- width: 100%;
- position: relative;
- height: 10px;
-}
-.timeline-svg .point {
- cursor: pointer;
-}
-.timeline-svg.disabled .point {
- fill: #BDBDBD;
- cursor: not-allowed;
-}
-.timeline-svg:not(.disabled) .point.selected {
- fill: #b2f6faff;
-}
-.timeline-svg.disabled .point.selected {
- fill: rgba(240, 59, 59, 0.596);
-}
-.error {
- stroke: rgb(255, 0, 0);
- stroke-width: 8px;
- cursor: pointer;
-}
-</style>
\ No newline at end of file
diff --git a/tools/winscope/src/TimelineSelection.vue b/tools/winscope/src/TimelineSelection.vue
deleted file mode 100644
index 8829009..0000000
--- a/tools/winscope/src/TimelineSelection.vue
+++ /dev/null
@@ -1,463 +0,0 @@
-<!-- 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'] = '10';
-
- 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
deleted file mode 100644
index 782fba6..0000000
--- a/tools/winscope/src/Timelines.vue
+++ /dev/null
@@ -1,376 +0,0 @@
-<!-- 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': 10,
- '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: 10;
- 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/Title.vue b/tools/winscope/src/Title.vue
deleted file mode 100644
index d522950..0000000
--- a/tools/winscope/src/Title.vue
+++ /dev/null
@@ -1,31 +0,0 @@
-<script>
-export default {
- name: 'vue-title',
- props: ['appName', 'traceName'],
- watch: {
- traceName: {
- immediate: true,
- handler() {
- this.updatePageTitle();
- }
- },
- appName: {
- immediate: true,
- handler() {
- this.updatePageTitle();
- }
- }
- },
- methods: {
- updatePageTitle() {
- if (this.traceName == null || this.traceName == "") {
- document.title = this.appName;
- } else {
- document.title = this.traceName;
- }
- }
- },
- render () {
- },
-}
-</script>
diff --git a/tools/winscope/src/TraceView.vue b/tools/winscope/src/TraceView.vue
deleted file mode 100644
index 59be504..0000000
--- a/tools/winscope/src/TraceView.vue
+++ /dev/null
@@ -1,557 +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>
- <md-card-content class="container">
- <div class="rects" v-if="hasScreenView">
- <rects
- :bounds="bounds"
- :rects="rects"
- :displays="displays"
- :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="showHierarchyDiff"
- 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-checkbox v-if="hasTagsOrErrors" v-model="store.flickerTraceView">Flicker</md-checkbox>
- <md-field md-inline class="filter">
- <label>Filter...</label>
- <md-input
- v-model="hierarchyPropertyFilterString"
- v-on:focus="updateInputMode(true)"
- v-on:blur="updateInputMode(false)"
- />
- </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"
- :onlyVisible="store.onlyVisible"
- :flickerTraceView="store.flickerTraceView"
- :presentTags="presentTags"
- :presentErrors="presentErrors"
- :items-clickable="true"
- :useGlobalCollapsedState="true"
- :simplify-names="store.simplifyNames"
- ref="hierarchy"
- />
- </div>
- </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>
- <div>
- <md-checkbox
- v-model="displayDefaults"
- @change="checkboxChange"
- >
- Show Defaults
- </md-checkbox>
- <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>
- </div>
- <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"
- v-on:focus="updateInputMode(true)"
- v-on:blur="updateInputMode(false)"
- />
- </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"
- :elementView="PropertiesTreeElement"
- />
- </div>
- <div class="no-properties" v-else>
- <i class="material-icons none-icon">
- filter_none
- </i>
- <span>No element selected in the hierarchy.</span>
- </div>
- </div>
- </flat-card>
- </div>
-
- </md-card-content>
-</template>
-<script>
-import TreeView from './TreeView.vue';
-import Rects from './Rects.vue';
-import FlatCard from './components/FlatCard.vue';
-import PropertiesTreeElement from './PropertiesTreeElement.vue';
-
-import {ObjectTransformer} from './transform.js';
-import {DiffGenerator, defaultModifiedCheck} from './utils/diff.js';
-import {TRACE_TYPES, DUMP_TYPES} from './decode.js';
-import {isPropertyMatch, stableIdCompatibilityFixup} from './utils/utils.js';
-import {CompatibleFeatures} from './utils/compatibility.js';
-import {getPropertiesForDisplay} from './flickerlib/mixin';
-import ObjectFormatter from './flickerlib/ObjectFormatter';
-
-function formatProto(obj) {
- if (obj?.prettyPrint) {
- return obj.prettyPrint();
- }
-}
-
-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', 'presentTags', 'presentErrors'],
- data() {
- return {
- propertyFilterString: '',
- hierarchyPropertyFilterString: '',
- selectedTree: null,
- hierarchySelected: null,
- lastSelectedStableId: null,
- bounds: {},
- rects: [],
- displays: [],
- item: null,
- tree: null,
- highlight: null,
- showHierarchyDiff: false,
- displayDefaults: false,
- showPropertiesDiff: false,
- PropertiesTreeElement,
- };
- },
- methods: {
- checkboxChange(checked) {
- this.itemSelected(this.item);
- },
- itemSelected(item) {
- this.hierarchySelected = item;
- this.selectedTree = this.getTransformedProperties(item);
- this.highlight = item.rect;
- this.lastSelectedStableId = item.stableId;
- // Record analytics event
- if (item.type || item.kind || item.stableId) {
- this.recordOpenedEntryEvent(item.type ?? item.kind ?? item.stableId);
- }
- this.$emit('focus');
- },
- getTransformedProperties(item) {
- ObjectFormatter.displayDefaults = this.displayDefaults;
- // There are 2 types of object whose properties can appear in the property
- // list: Flicker objects (WM/SF traces) and dictionaries
- // (IME/Accessibilty/Transactions).
- // While flicker objects have their properties directly in the main object,
- // those created by a call to the transform function have their properties
- // inside an obj property. This makes both cases work
- // TODO(209452852) Refactor both flicker and winscope-native objects to
- // implement a common display interface that can be better handled
- const target = item.obj ?? item;
- const transformer = new ObjectTransformer(
- getPropertiesForDisplay(target),
- item.name,
- stableIdCompatibilityFixup(item),
- ).setOptions({
- skip: item.skip,
- formatter: formatProto,
- });
-
- if (this.showPropertiesDiff && this.diffVisualizationAvailable) {
- const prevItem = this.getItemFromPrevTree(item);
- transformer.withDiff(getPropertiesForDisplay(prevItem));
- }
-
- return transformer.transform();
- },
- onRectClick(item) {
- if (item) {
- this.itemSelected(item);
- }
- },
- generateTreeFromItem(item) {
- if (!this.showHierarchyDiff || !this.diffVisualizationAvailable) {
- return item;
- }
-
- const thisItem = this.item;
- const prevItem = this.getDataWithOffset(-1);
- return new DiffGenerator(thisItem)
- .compareWith(prevItem)
- .withUniqueNodeId((node) => {
- return node.stableId;
- })
- .withModifiedCheck(defaultModifiedCheck)
- .generateDiffTree();
- },
- setData(item) {
- this.item = item;
- this.tree = this.generateTreeFromItem(item);
-
- const rects = item.rects; // .toArray()
- this.rects = [...rects].reverse();
- this.bounds = item.bounds;
-
- //only update displays if item is SF trace and displays present
- if (item.stableId==="LayerTraceEntry") {
- this.displays = item.displays;
- } else {
- this.displays = [];
- }
-
- this.hierarchySelected = null;
- this.selectedTree = null;
- this.highlight = null;
-
- function findItem(item, stableId) {
- if (item.stableId === stableId) {
- return item;
- }
- if (Array.isArray(item.children)) {
- for (const child of item.children) {
- const found = findItem(child, stableId);
- if (found) {
- return found;
- }
- }
- }
- return null;
- }
-
- if (this.lastSelectedStableId) {
- const found = findItem(item, this.lastSelectedStableId);
- if (found) {
- this.itemSelected(found);
- }
- }
- },
- arrowUp() {
- return this.$refs.hierarchy.selectPrev();
- },
- 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;
- },
-
- /** Performs check for id match between entry and present tags/errors
- * must be carried out for every present tag/error
- */
- matchItems(flickerItems, entryItem) {
- var match = false;
- flickerItems.forEach(flickerItem => {
- if (isPropertyMatch(flickerItem, entryItem)) match = true;
- });
- return match;
- },
- /** Returns check for id match between entry and present tags/errors */
- isEntryTagMatch(entryItem) {
- return this.matchItems(this.presentTags, entryItem) || this.matchItems(this.presentErrors, entryItem);
- },
-
- /** determines whether left/right arrow keys should move cursor in input field */
- updateInputMode(isInputMode) {
- this.store.isInputMode = isInputMode;
- },
- },
- created() {
- const item = this.file.data[this.file.selectedIndex ?? 0];
- // Record analytics event
- if (item.type || item.kind || item.stableId) {
- this.recordOpenTraceEvent(item.type ?? item.kind ?? item.stableId);
- }
- this.setData(item);
- },
- destroyed() {
- this.store.flickerTraceView = false;
- },
- watch: {
- selectedIndex() {
- this.setData(this.file.data[this.file.selectedIndex ?? 0]);
- },
- showHierarchyDiff() {
- this.tree = this.generateTreeFromItem(this.item);
- },
- showPropertiesDiff() {
- if (this.hierarchySelected) {
- this.selectedTree =
- this.getTransformedProperties(this.hierarchySelected);
- }
- },
- },
- 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() {
- const hierarchyPropertyFilter =
- getFilter(this.hierarchyPropertyFilterString);
- var fil = this.store.onlyVisible ? (c) => {
- return c.isVisible && hierarchyPropertyFilter(c);
- } : hierarchyPropertyFilter;
- return this.store.flickerTraceView ? (c) => {
- return this.isEntryTagMatch(c);
- } : fil;
- },
- propertyFilter() {
- return getFilter(this.propertyFilterString);
- },
- hasScreenView() {
- 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;
- },
- hasTagsOrErrors() {
- return this.presentTags.length > 0 || this.presentErrors.length > 0;
- },
- },
- components: {
- 'tree-view': TreeView,
- 'rects': Rects,
- 'flat-card': FlatCard,
- },
-};
-
-function getFilter(filterString) {
- const filterStrings = filterString.split(',');
- const positive = [];
- const negative = [];
- filterStrings.forEach((f) => {
- if (f.startsWith('!')) {
- const regex = new RegExp(f.substring(1), "i");
- negative.push((s) => !regex.test(s));
- } else {
- const regex = new RegExp(f, "i");
- positive.push((s) => regex.test(s));
- }
- });
- const filter = (item) => {
- const apply = (f) => f(String(item.name));
- return (positive.length === 0 || positive.some(apply)) &&
- (negative.length === 0 || negative.every(apply));
- };
- return filter;
-}
-
-</script>
-<style scoped>
-.container {
- display: flex;
- flex-wrap: wrap;
-}
-
-.rects {
- flex: none;
- margin: 8px;
-}
-
-.hierarchy,
-.properties {
- 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,
-.properties>.tree-view {
- margin: 16px;
-}
-
-.treeview {
- overflow: auto;
- 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/TransactionEntryLegacy.vue b/tools/winscope/src/TransactionEntryLegacy.vue
deleted file mode 100644
index 08c6af9..0000000
--- a/tools/winscope/src/TransactionEntryLegacy.vue
+++ /dev/null
@@ -1,292 +0,0 @@
-<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"
- >
- <span
- v-if="simplifyNames && surface.shortName &&
- surface.shortName !== surface.name"
- >{{surface.shortName}}>
- </span>
- <span v-else>
- <!-- 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">, </span>
- </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>
-import { shortenName } from './flickerlib/mixin'
-
-export default {
- name: 'transaction-entry-legacy',
- props: {
- index: {
- type: Number,
- },
- source: {
- type: Object,
- default() {
- return {};
- },
- },
- onClick: {
- type: Function,
- },
- selectedTransaction: {
- type: Object,
- },
- transactionsTrace: {
- type: Object,
- },
- prettifyTransactionId: {
- type: Function,
- },
- simplifyNames: {
- type: Boolean,
- },
- },
- 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') {
- return [
- {
- name: transaction.layerName,
- shortName: shortenName(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,
- shortName: shortenName(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/TransactionsViewLegacy.vue b/tools/winscope/src/TransactionsViewLegacy.vue
deleted file mode 100644
index 782a58f..0000000
--- a/tools/winscope/src/TransactionsViewLegacy.vue
+++ /dev/null
@@ -1,545 +0,0 @@
-<!-- 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">
- <md-content
- md-tag="md-toolbar"
- md-elevation="0"
- class="card-toolbar md-transparent md-dense"
- >
- <h2 class="md-title" style="flex: 1">Transactions</h2>
- </md-content>
- <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-field>
- <label>Changed property</label>
- <md-select v-model="selectedProperties" multiple>
- <md-option
- v-for="property in properties"
- :value="property"
- v-bind:key="property">
- {{ property }}
- </md-option>
- </md-select>
- </md-field>
- </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>
-
- <md-checkbox v-model="trace.simplifyNames">
- Simplify names
- </md-checkbox>
-
- </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,
- simplifyNames: trace.simplifyNames,
- }"
- 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 TransactionEntryLegacy from './TransactionEntryLegacy.vue';
-import FlatCard from './components/FlatCard.vue';
-
-import {ObjectTransformer} from './transform.js';
-import {expandTransactionId} from '@/traces/TransactionsLegacy.ts';
-
-/**
- * @deprecated This trace has been replaced by the new transactions trace
- */
-export default {
- name: 'transactionsviewlegacy',
- 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: [],
- selectedProperties: [],
- selectedTransaction: null,
- transactionEntryComponent: TransactionEntryLegacy,
- transactionsTrace,
- expandTransactionId,
- };
- },
- computed: {
- data() {
- // Record analytics event
- this.recordOpenTraceEvent("TransactionsTrace");
- 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)) {
- // If filter isn't a number then check if the transaction's
- // target surface's name matches the filter — if so keep it.
- const regexFilter = new RegExp(filter, "i");
- if (regexFilter.test(transaction.layerName)) {
- 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.selectedProperties.length > 0) {
- const regexFilter = new RegExp(this.selectedProperties.join("|"), "i");
- filteredData = filteredData.filter(
- this.filterTransactions((transaction) => {
- for (const key in transaction.obj) {
- if (this.isMeaningfulChange(transaction.obj, key) && regexFilter.test(key)) {
- 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] = {
- 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
deleted file mode 100644
index dbca1a3..0000000
--- a/tools/winscope/src/TreeView.vue
+++ /dev/null
@@ -1,738 +0,0 @@
-<!-- Copyright (C) 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.
--->
-<template>
- <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 && !flattened"
- 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"
- :errors="errors"
- :transitions="transitions"
- />
- </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>
-
- <node-context-menu
- ref="nodeContextMenu"
- v-on:collapseAllOtherNodes="collapseAllOtherNodes"
- />
-
- <div class="children" v-if="children" v-show="!isCollapsed" :style="childrenIndentation()">
- <tree-view
- v-for="(c,i) in children"
- :item="c"
- @item-selected="childItemSelected"
- :selected="selected"
- :key="i"
- :filter="childFilter(c)"
- :flattened="flattened"
- :onlyVisible="onlyVisible"
- :simplify-names="simplifyNames"
- :flickerTraceView="flickerTraceView"
- :presentTags="currentTags"
- :presentErrors="currentErrors"
- :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:collapseSibling="collapseSibling"
- v-on:collapseAllOtherNodes="collapseAllOtherNodes"
- v-on:closeAllContextMenus="closeAllContextMenus"
- ref="children"
- />
- </div>
- </div>
-</template>
-
-<script>
-import DefaultTreeElement from './DefaultTreeElement.vue';
-import NodeContextMenu from './NodeContextMenu.vue';
-import {DiffType} from './utils/diff.js';
-import {isPropertyMatch} from './utils/utils.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',
- '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',
- 'onlyVisible',
- 'flickerTraceView',
- 'presentTags',
- 'presentErrors',
- ],
- data() {
- 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]: '.',
- },
- currentTags: [],
- currentErrors: [],
- transitions: [],
- errors: [],
- };
- },
- 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.currentTags = this.getCurrentItems(this.presentTags);
- this.currentErrors = this.getCurrentItems(this.presentErrors);
- this.transitions = this.getCurrentTransitions();
- this.errors = this.getCurrentErrorTags();
- this.updateCollapsedDiffClass();
- },
- isSelected(isSelected) {
- if (isSelected) {
- this.$emit('selected');
- } else {
- this.$emit('unselected');
- }
- },
- },
- methods: {
- setCollapseValue(isCollapsed) {
- if (this.useGlobalCollapsedState) {
- this.$store.commit('setCollapsedState', {
- item: this.item,
- isCollapsed,
- });
- } else {
- this.localCollapsedState = isCollapsed;
- }
- },
- toggleTree() {
- this.setCollapseValue(!this.isCollapsed);
- if (!this.isCollapsed) {
- this.recordExpandedPropertyEvent(this.item.name)
- }
- },
- 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;
- }
-
- // 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 (const c of this.$refs.children) {
- found = c.selectNext(found, inCollapsedTree);
- }
- }
-
- return 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 (const c of [...this.$refs.children].reverse()) {
- found = c.selectPrev(found, inCollapsedTree);
- }
- }
-
- // 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;
- }
-
- // 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);
- },
- select() {
- this.$emit('item-selected', this.item);
- },
- clicked(e) {
- if (window.getSelection().type === 'range') {
- // Ignore click if is selection
- return;
- }
-
- if (!this.isLeaf && e.detail % 2 === 0) {
- // Double click collapsable 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 (this.filter) {
- const thisMatches = this.filter(c);
- const childMatches = (child) => this.filterMatches(child);
- return thisMatches || (!this.applyingFlattened &&
- c.children && c.children.some(childMatches));
- }
- return true;
- },
- childFilter(c) {
- if (this.filter) {
- if (this.filter(c)) {
- // Filter matched c, don't apply further filtering on c's children.
- return undefined;
- }
- }
- 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('collapseSibling', this.item);
- },
- collapseSibling(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();
- }
- },
- childrenIndentation() {
- if (this.flattened || this.forceFlattened) {
- return {
- marginLeft: '0px',
- paddingLeft: '0px',
- marginTop: '0px',
- }
- } else {
- //Aligns border with collapse arrows
- return {
- marginLeft: '12px',
- paddingLeft: '11px',
- borderLeft: '1px solid rgb(238, 238, 238)',
- marginTop: '0px',
- }
- }
- },
-
- /** Performs check for id match between entry and present tags/errors
- * exits once match has been found
- */
- matchItems(flickerItems) {
- var match = false;
- flickerItems.every(flickerItem => {
- if (isPropertyMatch(flickerItem, this.item)) {
- match = true;
- return false;
- }
- });
- return match;
- },
- /** Returns check for id match between entry and present tags/errors */
- isEntryTagMatch() {
- return this.matchItems(this.currentTags) || this.matchItems(this.currentErrors);
- },
-
- getCurrentItems(items) {
- if (!items) return [];
- else return items.filter(item => item.timestamp===this.currentTimestamp);
- },
- getCurrentTransitions() {
- var transitions = [];
- var ids = [];
- this.currentTags.forEach(tag => {
- if (!ids.includes(tag.id) && isPropertyMatch(tag, this.item)) {
- transitions.push(tag.transition);
- ids.push(tag.id);
- }
- });
- return transitions;
- },
- getCurrentErrorTags() {
- return this.currentErrors.filter(error => isPropertyMatch(error, this.item));
- },
- },
- computed: {
- hasDiff() {
- return this.item?.diff !== undefined;
- },
- 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;
- },
- 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 offset = levelOffset * (this.depth + this.isLeaf) + 'px';
-
- var display = "";
- if (!this.item.timestamp
- && this.flattened
- && (this.onlyVisible && !this.item.isVisible ||
- this.flickerTraceView && !this.isEntryTagMatch())) {
- display = 'none';
- }
-
- return {
- marginLeft: '-' + offset,
- paddingLeft: offset,
- display: display,
- };
- },
- },
- 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;
-}
-
-.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: #365179;
- color: white;
-}
-
-.childSelected {
- border-left: 1px solid rgb(233, 22, 22)
-}
-
-.selected .kind {
- color: #e9e9e9;
-}
-
-.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;
- 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
deleted file mode 100644
index 1d01ac0..0000000
--- a/tools/winscope/src/VideoView.vue
+++ /dev/null
@@ -1,87 +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>
- <video
- class="md-elevation-2 screen"
- :src="file.data"
- :style="style"
- ref="video"
- />
-</template>
-<script>
-const EPSILON = 0.00001;
-
-function uint8ToString(array) {
- 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('');
-}
-
-export default {
- name: 'videoview',
- props: ['file', 'height'],
- data() {
- // Record analytics event
- this.recordOpenTraceEvent("Video");
- 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;
- },
- arrowDown() {
- return true;
- },
- selectFrameAtTime(timestamp) {
- const time = (timestamp - this.file.timeline[0]) / 1000000000 + EPSILON;
- this.$refs.video.currentTime = time;
- },
- selectFrame(idx) {
- this.selectFrameAtTime(this.file.timeline[idx]);
- },
- jumpToSelectedIndex() {
- this.selectFrame(this.file.selectedIndex);
- },
- },
- watch: {
- selectedIndex() {
- this.selectFrame(this.file.selectedIndex);
- },
- },
- mounted() {
- this.$el.addEventListener('canplay', (e) => {
- this.$emit('loaded');
- });
- },
-};
-
-</script>
-<style>
-</style>
diff --git a/tools/winscope/src/WindowManagerTraceView.vue b/tools/winscope/src/WindowManagerTraceView.vue
deleted file mode 100644
index d2d709a..0000000
--- a/tools/winscope/src/WindowManagerTraceView.vue
+++ /dev/null
@@ -1,47 +0,0 @@
-<!-- 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"
- :presentTags="presentTags"
- :presentErrors="presentErrors"
- />
-</template>
-
-<script>
-import TraceView from "@/TraceView.vue"
-
-export default {
- name: "WindowManagerTraceView",
- props: ["store", "file", "presentTags", "presentErrors"],
- components: {
- TraceView
- },
- methods: {
- summarizer(item) {
- const summary = [];
-
- if (item.isIncompleteReason) {
- summary.push({key: 'Incomplete state reason', value: item.isIncompleteReason});
- }
-
- return summary;
- },
- }
-}
-</script>
\ No newline at end of file
diff --git a/tools/winscope/src/abt_chrome_extension/abt_chrome_extension_protocol.ts b/tools/winscope/src/abt_chrome_extension/abt_chrome_extension_protocol.ts
new file mode 100644
index 0000000..787cc2e
--- /dev/null
+++ b/tools/winscope/src/abt_chrome_extension/abt_chrome_extension_protocol.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils} from 'common/function_utils';
+import {
+ BuganizerAttachmentsDownloadEmitter,
+ OnBuganizerAttachmentsDownloaded,
+ OnBuganizerAttachmentsDownloadStart,
+} from 'interfaces/buganizer_attachments_download_emitter';
+import {MessageType, OpenBuganizerResponse, OpenRequest, WebCommandMessage} from './messages';
+
+export class AbtChromeExtensionProtocol implements BuganizerAttachmentsDownloadEmitter {
+ static readonly ABT_EXTENSION_ID = 'mbbaofdfoekifkfpgehgffcpagbbjkmj';
+ private onAttachmentsDownloadStart: OnBuganizerAttachmentsDownloadStart =
+ FunctionUtils.DO_NOTHING;
+ private onAttachmentsDownloaded: OnBuganizerAttachmentsDownloaded =
+ FunctionUtils.DO_NOTHING_ASYNC;
+
+ setOnBuganizerAttachmentsDownloadStart(callback: OnBuganizerAttachmentsDownloadStart) {
+ this.onAttachmentsDownloadStart = callback;
+ }
+
+ setOnBuganizerAttachmentsDownloaded(callback: OnBuganizerAttachmentsDownloaded) {
+ this.onAttachmentsDownloaded = callback;
+ }
+
+ run() {
+ const urlParams = new URLSearchParams(window.location.search);
+ if (urlParams.get('source') !== 'openFromExtension' || !chrome) {
+ return;
+ }
+
+ this.onAttachmentsDownloadStart();
+
+ const openRequestMessage: OpenRequest = {
+ action: MessageType.OPEN_REQUEST,
+ };
+
+ chrome.runtime.sendMessage(
+ AbtChromeExtensionProtocol.ABT_EXTENSION_ID,
+ openRequestMessage,
+ async (message) => await this.onMessageReceived(message)
+ );
+ }
+
+ private async onMessageReceived(message: WebCommandMessage) {
+ if (this.isOpenBuganizerResponseMessage(message)) {
+ await this.onOpenBuganizerResponseMessageReceived(message);
+ } else {
+ console.warn('ABT chrome extension protocol received unexpected message:', message);
+ }
+ }
+
+ private async onOpenBuganizerResponseMessageReceived(message: OpenBuganizerResponse) {
+ console.log('ABT chrome extension protocol received OpenBuganizerResponse message:', message);
+
+ if (message.attachments.length === 0) {
+ console.warn('ABT chrome extension protocol received no attachments');
+ }
+
+ const filesBlobPromises = message.attachments.map(async (attachment) => {
+ const fileQueryResponse = await fetch(attachment.objectUrl);
+ const blob = await fileQueryResponse.blob();
+
+ // Note: the received blob's media type is wrong. It is always set to "image/png".
+ // Context: http://google3/javascript/closure/html/safeurl.js?g=0&l=256&rcl=273756987
+ // Cloning the blob clears the media type.
+ const file = new File([blob], attachment.name);
+
+ return file;
+ });
+
+ const files = await Promise.all(filesBlobPromises);
+ await this.onAttachmentsDownloaded(files);
+ }
+
+ private isOpenBuganizerResponseMessage(
+ message: WebCommandMessage
+ ): message is OpenBuganizerResponse {
+ return message.action === MessageType.OPEN_BUGANIZER_RESPONSE;
+ }
+}
diff --git a/tools/winscope/src/abt_chrome_extension/abt_chrome_extension_protocol_stub.ts b/tools/winscope/src/abt_chrome_extension/abt_chrome_extension_protocol_stub.ts
new file mode 100644
index 0000000..c07e976
--- /dev/null
+++ b/tools/winscope/src/abt_chrome_extension/abt_chrome_extension_protocol_stub.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils} from 'common/function_utils';
+import {
+ BuganizerAttachmentsDownloadEmitter,
+ OnBuganizerAttachmentsDownloaded,
+ OnBuganizerAttachmentsDownloadStart,
+} from 'interfaces/buganizer_attachments_download_emitter';
+import {Runnable} from 'interfaces/runnable';
+
+export class AbtChromeExtensionProtocolStub
+ implements BuganizerAttachmentsDownloadEmitter, Runnable
+{
+ onBuganizerAttachmentsDownloadStart: OnBuganizerAttachmentsDownloadStart =
+ FunctionUtils.DO_NOTHING;
+ onBuganizerAttachmentsDownloaded: OnBuganizerAttachmentsDownloaded =
+ FunctionUtils.DO_NOTHING_ASYNC;
+
+ setOnBuganizerAttachmentsDownloadStart(callback: OnBuganizerAttachmentsDownloadStart) {
+ this.onBuganizerAttachmentsDownloadStart = callback;
+ }
+
+ setOnBuganizerAttachmentsDownloaded(callback: OnBuganizerAttachmentsDownloaded) {
+ this.onBuganizerAttachmentsDownloaded = callback;
+ }
+
+ run() {
+ // do nothing
+ }
+}
diff --git a/tools/winscope/src/abt_chrome_extension/messages.ts b/tools/winscope/src/abt_chrome_extension/messages.ts
new file mode 100644
index 0000000..fec7e17
--- /dev/null
+++ b/tools/winscope/src/abt_chrome_extension/messages.ts
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 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/262667224):
+// deduplicate all the type definitions in this file when we move Winscope to google3.
+// The definitions were duplicated from these source files:
+// - google3/wireless/android/tools/android_bug_tool/app/platform/web/interface/command.ts
+// - google3/wireless/android/tools/android_bug_tool/app/platform/web/interface/attachment_metadata.ts
+// - google3/wireless/android/tools/android_bug_tool/app/platform/web/interface/bug_report_metadata.ts
+
+/** Describes the type of message enclosed in a {@code WebCommandMessage}. */
+export enum MessageType {
+ UNKNOWN,
+ OPEN_REQUEST,
+ OPEN_BUGANIZER_RESPONSE,
+ OPEN_WEB_RESPONSE,
+ OPEN_URL_REQUEST,
+ OPEN_URL_RESPONSE,
+ CHECK_ISSUE_METADATA_REQUEST,
+ CHECK_ISSUE_METADATA_RESPONSE,
+ OPEN_TOOL_WEB_REQUEST,
+}
+
+/** Base of all messages sent between the web and extension. */
+export declare interface WebCommandMessage {
+ action: MessageType;
+}
+
+/** Request from web to background to download the file. */
+export interface OpenRequest extends WebCommandMessage {
+ action: MessageType.OPEN_REQUEST;
+}
+
+/** Response of download the issue's attachment from background. */
+export declare interface OpenBuganizerResponse extends WebCommandMessage {
+ action: MessageType.OPEN_BUGANIZER_RESPONSE;
+
+ /** issue id */
+ issueId: string;
+
+ /** issue title */
+ issueTitle: string | undefined;
+
+ /** issue access level */
+ issueAccessLevel: IssueAccessLimit | undefined;
+
+ /** Attachment list. */
+ attachments: AttachmentMetadata[];
+}
+
+/** Attachment metadata. */
+export interface AttachmentMetadata {
+ bugReportMetadata?: BugReportMetadata;
+ author: string;
+ name: string;
+ objectUrl: string;
+ restrictionSeverity: RestrictionSeverity;
+ resourceName: string;
+ entityStatus: EntityStatus;
+ attachmentId: string;
+ fileSize: number;
+ commentTimestamp?: DateTime;
+}
+
+/**
+ * Incorporates all of the metadata that can be retrieved from a bugreport
+ * file name.
+ */
+export interface BugReportMetadata {
+ uuid?: string;
+ hasWinscope: boolean;
+ hasTrace: boolean;
+ isRedacted: boolean;
+ device: string;
+ build: string;
+ // The date parsed from the bug report filename is only used for
+ // grouping common files together. It is not used for display purposes.
+ timestamp: DateTime;
+}
+
+/**
+ * Defines of the issue access limit. See:
+ * http://go/buganizer/concepts/access-control#accesslimit
+ */
+export const enum IssueAccessLimit {
+ INTERNAL = '',
+ VISIBLE_TO_PARTNERS = 'Visible to Partners',
+ VISIBLE_TO_PUBLIC = 'Visible to Public',
+}
+
+/**
+ * Types of issue content restriction verdicts. See:
+ * http://google3/google/devtools/issuetracker/v1/issuetracker.proto?l=1858&rcl=278024740
+ */
+export enum RestrictionSeverity {
+ /** Unspecified restricted content severity */
+ RESTRICTION_SEVERITY_UNSPECIFIED = 0,
+ /** No restricted content was detected/flagged in the content */
+ NONE_DETECTED = 1,
+ /** Restricted content was detected/flagged in the content */
+ RESTRICTED = 2,
+ /** RESTRICTED_PLUS content was detected/flagged in the content */
+ RESTRICTED_PLUS = 3,
+}
+
+/**
+ * Types of entity statuses for issue tracker attachments. See:
+ * https:google3/google/devtools/issuetracker/v1/issuetracker.proto;rcl=448855448;l=58
+ */
+export enum EntityStatus {
+ // Default value. Entity exists and is available for use.
+ ACTIVE = 0,
+ // Entity is invisible except for administrative actions, i.e. undelete.
+ DELETED = 1,
+ // Entity is irretrievably wiped.
+ PURGED = 2,
+}
+
+// Actual definition is in google3/third_party/javascript/closure/date/date
+export type DateTime = object;
diff --git a/tools/winscope/adb_proxy/winscope_proxy.py b/tools/winscope/src/adb/winscope_proxy.py
old mode 100755
new mode 100644
similarity index 94%
rename from tools/winscope/adb_proxy/winscope_proxy.py
rename to tools/winscope/src/adb/winscope_proxy.py
index 87ccecc..15f606c
--- a/tools/winscope/adb_proxy/winscope_proxy.py
+++ b/tools/winscope/src/adb/winscope_proxy.py
@@ -192,6 +192,17 @@
'su root service call Wayland 26 i32 1 >/dev/null\necho "Wayland trace started."',
'su root service call Wayland 26 i32 0 >/dev/null'
),
+ "eventlog": TraceTarget(
+ WinscopeFileMatcher("/data/local/tmp", "eventlog", "eventlog"),
+ 'rm -f /data/local/tmp/eventlog.winscope && EVENT_LOG_TRACING_START_TIME=$EPOCHREALTIME\necho "Event Log trace started."',
+ 'echo "EventLog\\n" > /data/local/tmp/eventlog.winscope && su root logcat -b events -v threadtime -v printable -v uid -v nsec -v epoch -b events -t $EVENT_LOG_TRACING_START_TIME >> /data/local/tmp/eventlog.winscope',
+ ),
+ "transition_traces": TraceTarget(
+ [WinscopeFileMatcher(WINSCOPE_DIR, "wm_transition_trace", "wm_transition_trace"),
+ WinscopeFileMatcher(WINSCOPE_DIR, "shell_transition_trace", "shell_transition_trace")],
+ 'su root cmd window shell tracing start && su root dumpsys activity service SystemUIService WMShell transitions tracing start\necho "Transition traces started."',
+ 'su root cmd window shell tracing stop && su root dumpsys activity service SystemUIService WMShell transitions tracing stop >/dev/null 2>&1'
+ ),
}
@@ -636,10 +647,15 @@
# Do not print anything to stdout/stderr in the handler
function stop_trace() {{
+ echo "start" >/data/local/tmp/winscope_signal_handler.log
+
+ # redirect stdout/stderr to log file
+ exec 1>>/data/local/tmp/winscope_signal_handler.log
+ exec 2>>/data/local/tmp/winscope_signal_handler.log
+
+ set -x
trap - EXIT HUP INT
-
-{}
-
+ {}
echo "TRACE_OK" > /data/local/tmp/winscope_status
}}
@@ -656,6 +672,7 @@
def process_with_device(self, server, path, device_id):
try:
requested_types = self.get_request(server)
+ log.debug(f"Clienting requested trace types {requested_types}")
requested_traces = [TRACE_TARGETS[t] for t in requested_types]
except KeyError as err:
raise BadRequest("Unsupported trace target\n" + str(err))
@@ -671,6 +688,7 @@
'\n'.join([t.trace_start for t in requested_traces]))
log.debug("Trace requested for {} with targets {}".format(
device_id, ','.join(requested_types)))
+ log.debug(f"Executing command \"{command}\" on {device_id}...")
TRACE_THREADS[device_id] = TraceThread(
device_id, command.encode('utf-8'))
TRACE_THREADS[device_id].start()
@@ -685,8 +703,18 @@
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
+
+ signal_handler_log = call_adb("shell su root cat /data/local/tmp/winscope_signal_handler.log", device=device_id).encode('utf-8')
+
+ out = b"### Shell script's stdout - start\n" + \
+ TRACE_THREADS[device_id].out + \
+ b"### Shell script's stdout - end\n" + \
+ b"### Shell script's stderr - start\n" + \
+ TRACE_THREADS[device_id].err + \
+ b"### Shell script's stderr - end\n" + \
+ b"### Signal handler log - start\n" + \
+ signal_handler_log + \
+ b"### Signal handler log - end\n"
command = TRACE_THREADS[device_id].trace_command
TRACE_THREADS.pop(device_id)
if success:
diff --git a/tools/winscope/src/app/app_module.ts b/tools/winscope/src/app/app_module.ts
new file mode 100644
index 0000000..ddc8c74
--- /dev/null
+++ b/tools/winscope/src/app/app_module.ts
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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 {DragDropModule} from '@angular/cdk/drag-drop';
+import {ScrollingModule} from '@angular/cdk/scrolling';
+import {CommonModule} from '@angular/common';
+import {HttpClientModule} from '@angular/common/http';
+import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {MatButtonModule} from '@angular/material/button';
+import {MatCardModule} from '@angular/material/card';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatGridListModule} from '@angular/material/grid-list';
+import {MatIconModule} from '@angular/material/icon';
+import {MatInputModule} from '@angular/material/input';
+import {MatListModule} from '@angular/material/list';
+import {MatProgressBarModule} from '@angular/material/progress-bar';
+import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
+import {MatRadioModule} from '@angular/material/radio';
+import {MatSelectModule} from '@angular/material/select';
+import {MatSliderModule} from '@angular/material/slider';
+import {MatSnackBarModule} from '@angular/material/snack-bar';
+import {MatTabsModule} from '@angular/material/tabs';
+import {MatToolbarModule} from '@angular/material/toolbar';
+import {MatTooltipModule} from '@angular/material/tooltip';
+import {BrowserModule} from '@angular/platform-browser';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {CoordinatesTableComponent} from 'viewers/components/coordinates_table_component';
+import {HierarchyComponent} from 'viewers/components/hierarchy_component';
+import {ImeAdditionalPropertiesComponent} from 'viewers/components/ime_additional_properties_component';
+import {PropertiesComponent} from 'viewers/components/properties_component';
+import {PropertiesTableComponent} from 'viewers/components/properties_table_component';
+import {PropertyGroupsComponent} from 'viewers/components/property_groups_component';
+import {RectsComponent} from 'viewers/components/rects/rects_component';
+import {TransformMatrixComponent} from 'viewers/components/transform_matrix_component';
+import {TreeComponent} from 'viewers/components/tree_component';
+import {TreeNodeComponent} from 'viewers/components/tree_node_component';
+import {TreeNodeDataViewComponent} from 'viewers/components/tree_node_data_view_component';
+import {TreeNodePropertiesDataViewComponent} from 'viewers/components/tree_node_properties_data_view_component';
+import {ViewerInputMethodComponent} from 'viewers/components/viewer_input_method_component';
+import {ViewerProtologComponent} from 'viewers/viewer_protolog/viewer_protolog_component';
+import {ViewerScreenRecordingComponent} from 'viewers/viewer_screen_recording/viewer_screen_recording_component';
+import {ViewerSurfaceFlingerComponent} from 'viewers/viewer_surface_flinger/viewer_surface_flinger_component';
+import {ViewerTransactionsComponent} from 'viewers/viewer_transactions/viewer_transactions_component';
+import {ViewerTransitionsComponent} from 'viewers/viewer_transitions/viewer_transitions_component';
+import {ViewerWindowManagerComponent} from 'viewers/viewer_window_manager/viewer_window_manager_component';
+import {AdbProxyComponent} from './components/adb_proxy_component';
+import {AppComponent} from './components/app_component';
+import {
+ MatDrawer,
+ MatDrawerContainer,
+ MatDrawerContent,
+} from './components/bottomnav/bottom_drawer_component';
+import {CollectTracesComponent} from './components/collect_traces_component';
+import {LoadProgressComponent} from './components/load_progress_component';
+import {SnackBarComponent} from './components/snack_bar_component';
+import {ExpandedTimelineComponent} from './components/timeline/expanded_timeline_component';
+import {MiniTimelineComponent} from './components/timeline/mini_timeline_component';
+import {SingleTimelineComponent} from './components/timeline/single_timeline_component';
+import {TimelineComponent} from './components/timeline/timeline_component';
+import {TraceConfigComponent} from './components/trace_config_component';
+import {TraceViewComponent} from './components/trace_view_component';
+import {UploadTracesComponent} from './components/upload_traces_component';
+import {WebAdbComponent} from './components/web_adb_component';
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ViewerWindowManagerComponent,
+ ViewerSurfaceFlingerComponent,
+ ViewerInputMethodComponent,
+ ViewerProtologComponent,
+ ViewerTransactionsComponent,
+ ViewerScreenRecordingComponent,
+ ViewerTransitionsComponent,
+ CollectTracesComponent,
+ UploadTracesComponent,
+ AdbProxyComponent,
+ WebAdbComponent,
+ TraceConfigComponent,
+ HierarchyComponent,
+ PropertiesComponent,
+ RectsComponent,
+ TraceViewComponent,
+ TreeComponent,
+ TreeNodeComponent,
+ TreeNodeDataViewComponent,
+ TreeNodePropertiesDataViewComponent,
+ PropertyGroupsComponent,
+ TransformMatrixComponent,
+ PropertiesTableComponent,
+ ImeAdditionalPropertiesComponent,
+ CoordinatesTableComponent,
+ TimelineComponent,
+ MiniTimelineComponent,
+ ExpandedTimelineComponent,
+ SingleTimelineComponent,
+ SnackBarComponent,
+ MatDrawer,
+ MatDrawerContent,
+ MatDrawerContainer,
+ LoadProgressComponent,
+ ],
+ imports: [
+ BrowserModule,
+ HttpClientModule,
+ CommonModule,
+ MatCardModule,
+ MatButtonModule,
+ MatGridListModule,
+ FormsModule,
+ MatListModule,
+ MatCheckboxModule,
+ MatDividerModule,
+ MatIconModule,
+ MatProgressSpinnerModule,
+ MatProgressBarModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatSelectModule,
+ BrowserAnimationsModule,
+ HttpClientModule,
+ MatSliderModule,
+ MatRadioModule,
+ MatTooltipModule,
+ MatToolbarModule,
+ MatTabsModule,
+ MatSnackBarModule,
+ ScrollingModule,
+ DragDropModule,
+ ReactiveFormsModule,
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ bootstrap: [AppComponent],
+})
+export class AppModule {}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/app/colors.ts
similarity index 67%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/app/colors.ts
index bfe19ce..09fb923 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/app/colors.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export enum Color {
+ SELECTOR_COLOR = '#8AB4F8',
+ SELECTION_BACKGROUND = '#E8F0FE',
+ ACTIVE_POINTER = '#1967D2',
+ GUIDE_BAR = '#3C4043',
+ GUIDE_BAR_LIGHT = '#9AA0A6',
+ ACTIVE_BORDER = ACTIVE_POINTER,
+}
diff --git a/tools/winscope/src/app/components/adb_proxy_component.ts b/tools/winscope/src/app/components/adb_proxy_component.ts
new file mode 100644
index 0000000..3f0c0a3
--- /dev/null
+++ b/tools/winscope/src/app/components/adb_proxy_component.ts
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 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 {Component, EventEmitter, Input, Output} from '@angular/core';
+import {proxyClient, ProxyClient, ProxyState} from 'trace_collection/proxy_client';
+
+@Component({
+ selector: 'adb-proxy',
+ template: `
+ <ng-container [ngSwitch]="proxy.state">
+ <ng-container *ngSwitchCase="states.NO_PROXY">
+ <div class="further-adb-info-text">
+ <p class="mat-body-1">
+ Launch the Winscope ADB Connect proxy to capture traces directly from your browser.
+ </p>
+ <p class="mat-body-1">Python 3.5+ and ADB are required.</p>
+ <p class="mat-body-1">
+ Run:
+ <code>
+ python3 $ANDROID_BUILD_TOP/development/tools/winscope/src/adb/winscope_proxy.py
+ </code>
+ </p>
+ <p class="mat-body-1">Or get it from the AOSP repository.</p>
+ </div>
+
+ <div class="further-adb-info-actions">
+ <button color="primary" mat-stroked-button (click)="downloadFromAosp()">
+ Download from AOSP
+ </button>
+ <button color="primary" mat-stroked-button class="retry" (click)="restart()">
+ Retry
+ </button>
+ </div>
+ </ng-container>
+
+ <ng-container *ngSwitchCase="states.INVALID_VERSION">
+ <div class="further-adb-info-text">
+ <p class="icon-information mat-body-1">
+ <mat-icon class="adb-icon">update</mat-icon>
+ <span class="adb-info">Your local proxy version is incompatible with Winscope.</span>
+ </p>
+ <p class="mat-body-1">Please update the proxy to version {{ proxyVersion }}.</p>
+ <p class="mat-body-1">
+ Run:
+ <code>
+ python3 $ANDROID_BUILD_TOP/development/tools/winscope/src/adb/winscope_proxy.py
+ </code>
+ </p>
+ <p class="mat-body-1">Or get it from the AOSP repository.</p>
+ </div>
+
+ <div class="further-adb-info-actions">
+ <button color="primary" mat-stroked-button (click)="downloadFromAosp()">
+ Download from AOSP
+ </button>
+ <button color="primary" mat-stroked-button class="retry" (click)="restart()">
+ Retry
+ </button>
+ </div>
+ </ng-container>
+
+ <ng-container *ngSwitchCase="states.UNAUTH">
+ <div class="further-adb-info-text">
+ <p class="icon-information mat-body-1">
+ <mat-icon class="adb-icon">lock</mat-icon>
+ <span class="adb-info">Proxy authorisation required.</span>
+ </p>
+ <p class="mat-body-1">Enter Winscope proxy token:</p>
+ <mat-form-field>
+ <input matInput [(ngModel)]="proxyKeyItem" name="proxy-key" />
+ </mat-form-field>
+ <p class="mat-body-1">
+ The proxy token is printed to console on proxy launch, copy and paste it above.
+ </p>
+ </div>
+
+ <div class="further-adb-info-actions">
+ <button color="primary" mat-stroked-button class="retry" (click)="restart()">
+ Connect
+ </button>
+ </div>
+ </ng-container>
+
+ <ng-container *ngSwitchDefault></ng-container>
+ </ng-container>
+ `,
+ styles: [
+ `
+ .icon-information {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
+ .further-adb-info-text {
+ display: flex;
+ flex-direction: column;
+ overflow-wrap: break-word;
+ gap: 10px;
+ margin-bottom: 10px;
+ }
+ .further-adb-info-actions {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ .adb-info {
+ margin-left: 5px;
+ }
+ `,
+ ],
+})
+export class AdbProxyComponent {
+ @Input()
+ proxy: ProxyClient = proxyClient;
+
+ @Output()
+ proxyChange = new EventEmitter<ProxyClient>();
+
+ @Output()
+ addKey = new EventEmitter<string>();
+
+ states = ProxyState;
+ proxyKeyItem = '';
+ readonly proxyVersion = this.proxy.VERSION;
+ readonly downloadProxyUrl: string =
+ 'https://android.googlesource.com/platform/development/+/master/tools/winscope/adb_proxy/winscope_proxy.py';
+
+ restart() {
+ this.addKey.emit(this.proxyKeyItem);
+ this.proxy.setState(this.states.CONNECTING);
+ this.proxyChange.emit(this.proxy);
+ }
+
+ downloadFromAosp() {
+ window.open(this.downloadProxyUrl, '_blank')?.focus();
+ }
+}
diff --git a/tools/winscope/src/app/components/adb_proxy_component_test.ts b/tools/winscope/src/app/components/adb_proxy_component_test.ts
new file mode 100644
index 0000000..f564abe
--- /dev/null
+++ b/tools/winscope/src/app/components/adb_proxy_component_test.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatButtonModule} from '@angular/material/button';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatIconModule} from '@angular/material/icon';
+import {MatInputModule} from '@angular/material/input';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {proxyClient, ProxyState} from 'trace_collection/proxy_client';
+import {AdbProxyComponent} from './adb_proxy_component';
+
+describe('AdbProxyComponent', () => {
+ let fixture: ComponentFixture<AdbProxyComponent>;
+ let component: AdbProxyComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ CommonModule,
+ MatIconModule,
+ MatFormFieldModule,
+ MatInputModule,
+ BrowserAnimationsModule,
+ MatButtonModule,
+ ],
+ declarations: [AdbProxyComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ fixture = TestBed.createComponent(AdbProxyComponent);
+ component = fixture.componentInstance;
+ component.proxy = proxyClient;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('check correct icon and message displays if no proxy', () => {
+ component.proxy.setState(ProxyState.NO_PROXY);
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.further-adb-info-text')?.innerHTML).toContain(
+ 'Launch the Winscope ADB Connect proxy'
+ );
+ });
+
+ it('check correct icon and message displays if invalid proxy', () => {
+ component.proxy.setState(ProxyState.INVALID_VERSION);
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.adb-info')?.innerHTML).toBe(
+ 'Your local proxy version is incompatible with Winscope.'
+ );
+ expect(htmlElement.querySelector('.adb-icon')?.innerHTML).toBe('update');
+ });
+
+ it('check correct icon and message displays if unauthorised proxy', () => {
+ component.proxy.setState(ProxyState.UNAUTH);
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.adb-info')?.innerHTML).toBe('Proxy authorisation required.');
+ expect(htmlElement.querySelector('.adb-icon')?.innerHTML).toBe('lock');
+ });
+
+ it('check retry button acts as expected', async () => {
+ component.proxy.setState(ProxyState.NO_PROXY);
+ fixture.detectChanges();
+ spyOn(component, 'restart').and.callThrough();
+ const button: HTMLButtonElement | null = htmlElement.querySelector('.retry');
+ expect(button).toBeInstanceOf(HTMLButtonElement);
+ button?.dispatchEvent(new Event('click'));
+ await fixture.whenStable();
+ expect(component.restart).toHaveBeenCalled();
+ });
+});
diff --git a/tools/winscope/src/app/components/app_component.ts b/tools/winscope/src/app/components/app_component.ts
new file mode 100644
index 0000000..f249c0c
--- /dev/null
+++ b/tools/winscope/src/app/components/app_component.ts
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2022 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 {
+ ChangeDetectorRef,
+ Component,
+ Inject,
+ Injector,
+ ViewChild,
+ ViewEncapsulation,
+} from '@angular/core';
+import {createCustomElement} from '@angular/elements';
+import {AbtChromeExtensionProtocol} from 'abt_chrome_extension/abt_chrome_extension_protocol';
+import {Mediator} from 'app/mediator';
+import {TimelineData} from 'app/timeline_data';
+import {TRACE_INFO} from 'app/trace_info';
+import {TracePipeline} from 'app/trace_pipeline';
+import {FileUtils} from 'common/file_utils';
+import {PersistentStore} from 'common/persistent_store';
+import {CrossToolProtocol} from 'cross_tool/cross_tool_protocol';
+import {TraceDataListener} from 'interfaces/trace_data_listener';
+import {LoadedTrace} from 'trace/loaded_trace';
+import {Timestamp} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {proxyClient, ProxyState} from 'trace_collection/proxy_client';
+import {ViewerInputMethodComponent} from 'viewers/components/viewer_input_method_component';
+import {View, Viewer} from 'viewers/viewer';
+import {ViewerProtologComponent} from 'viewers/viewer_protolog/viewer_protolog_component';
+import {ViewerScreenRecordingComponent} from 'viewers/viewer_screen_recording/viewer_screen_recording_component';
+import {ViewerSurfaceFlingerComponent} from 'viewers/viewer_surface_flinger/viewer_surface_flinger_component';
+import {ViewerTransactionsComponent} from 'viewers/viewer_transactions/viewer_transactions_component';
+import {ViewerTransitionsComponent} from 'viewers/viewer_transitions/viewer_transitions_component';
+import {ViewerWindowManagerComponent} from 'viewers/viewer_window_manager/viewer_window_manager_component';
+import {CollectTracesComponent} from './collect_traces_component';
+import {SnackBarOpener} from './snack_bar_opener';
+import {TimelineComponent} from './timeline/timeline_component';
+import {UploadTracesComponent} from './upload_traces_component';
+
+@Component({
+ selector: 'app-root',
+ template: `
+ <mat-toolbar class="toolbar">
+ <span class="app-title">Winscope</span>
+
+ <a href="http://go/winscope-legacy">
+ <button color="primary" mat-button>Open legacy Winscope</button>
+ </a>
+
+ <div class="spacer">
+ <mat-icon
+ *ngIf="activeTrace"
+ class="icon"
+ [matTooltip]="TRACE_INFO[activeTrace.type].name"
+ [style]="{color: TRACE_INFO[activeTrace.type].color, marginRight: '0.5rem'}">
+ {{ TRACE_INFO[activeTrace.type].icon }}
+ </mat-icon>
+ <span *ngIf="dataLoaded" class="active-trace-file-info mat-body-2">
+ {{ activeTraceFileInfo }}
+ </span>
+ </div>
+
+ <button
+ *ngIf="dataLoaded"
+ color="primary"
+ mat-stroked-button
+ (click)="mediator.onWinscopeUploadNew()">
+ Upload New
+ </button>
+
+ <button
+ mat-icon-button
+ matTooltip="Report bug"
+ (click)="goToLink('https://b.corp.google.com/issues/new?component=909476')">
+ <mat-icon> bug_report</mat-icon>
+ </button>
+
+ <button
+ mat-icon-button
+ matTooltip="Switch to {{ isDarkModeOn ? 'light' : 'dark' }} mode"
+ (click)="setDarkMode(!isDarkModeOn)">
+ <mat-icon>
+ {{ isDarkModeOn ? 'brightness_5' : 'brightness_4' }}
+ </mat-icon>
+ </button>
+ </mat-toolbar>
+
+ <mat-divider></mat-divider>
+
+ <mat-drawer-container class="example-container" autosize disableClose autoFocus>
+ <mat-drawer-content>
+ <ng-container *ngIf="dataLoaded; else noLoadedTracesBlock">
+ <trace-view
+ class="viewers"
+ [viewers]="viewers"
+ [store]="store"
+ (downloadTracesButtonClick)="onDownloadTracesButtonClick()"
+ (activeViewChanged)="onActiveViewChanged($event)"></trace-view>
+
+ <mat-divider></mat-divider>
+ </ng-container>
+ </mat-drawer-content>
+
+ <mat-drawer #drawer mode="overlay" opened="true" [baseHeight]="collapsedTimelineHeight">
+ <timeline
+ *ngIf="dataLoaded"
+ [timelineData]="timelineData"
+ [activeViewTraceTypes]="activeView?.dependencies"
+ [availableTraces]="getLoadedTraceTypes()"
+ (collapsedTimelineSizeChanged)="onCollapsedTimelineSizeChanged($event)"></timeline>
+ </mat-drawer>
+ </mat-drawer-container>
+
+ <ng-template #noLoadedTracesBlock>
+ <div class="center">
+ <div class="landing-content">
+ <h1 class="welcome-info mat-headline">
+ Welcome to Winscope. Please select source to view traces.
+ </h1>
+
+ <div class="card-grid landing-grid">
+ <collect-traces
+ class="collect-traces-card homepage-card"
+ (filesCollected)="mediator.onWinscopeFilesCollected($event)"
+ [store]="store"></collect-traces>
+
+ <upload-traces
+ class="upload-traces-card homepage-card"
+ [tracePipeline]="tracePipeline"
+ (filesUploaded)="mediator.onWinscopeFilesUploaded($event)"
+ (viewTracesButtonClick)="mediator.onWinscopeViewTracesRequest()"></upload-traces>
+ </div>
+ </div>
+ </div>
+ </ng-template>
+ `,
+ styles: [
+ `
+ .toolbar {
+ gap: 10px;
+ }
+ .welcome-info {
+ margin: 16px 0 6px 0;
+ text-align: center;
+ }
+ .homepage-card {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ overflow: auto;
+ height: 820px;
+ }
+ .spacer {
+ flex: 1;
+ text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ .viewers {
+ height: 0;
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ }
+ .center {
+ display: flex;
+ align-content: center;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ justify-items: center;
+ flex-grow: 1;
+ }
+ .landing-content {
+ width: 100%;
+ }
+ .landing-content .card-grid {
+ max-width: 1800px;
+ flex-grow: 1;
+ margin: auto;
+ }
+ `,
+ ],
+ encapsulation: ViewEncapsulation.None,
+})
+export class AppComponent implements TraceDataListener {
+ title = 'winscope';
+ changeDetectorRef: ChangeDetectorRef;
+ snackbarOpener: SnackBarOpener;
+ tracePipeline = new TracePipeline();
+ timelineData = new TimelineData();
+ abtChromeExtensionProtocol = new AbtChromeExtensionProtocol();
+ crossToolProtocol = new CrossToolProtocol();
+ mediator: Mediator;
+ states = ProxyState;
+ store: PersistentStore = new PersistentStore();
+ currentTimestamp?: Timestamp;
+ viewers: Viewer[] = [];
+ isDarkModeOn!: boolean;
+ dataLoaded = false;
+ activeView?: View;
+ activeTrace?: LoadedTrace;
+ activeTraceFileInfo = '';
+ collapsedTimelineHeight = 0;
+ @ViewChild(UploadTracesComponent) uploadTracesComponent?: UploadTracesComponent;
+ @ViewChild(CollectTracesComponent) collectTracesComponent?: UploadTracesComponent;
+ @ViewChild(TimelineComponent) timelineComponent?: TimelineComponent;
+ TRACE_INFO = TRACE_INFO;
+
+ constructor(
+ @Inject(Injector) injector: Injector,
+ @Inject(ChangeDetectorRef) changeDetectorRef: ChangeDetectorRef,
+ @Inject(SnackBarOpener) snackBar: SnackBarOpener
+ ) {
+ this.changeDetectorRef = changeDetectorRef;
+ this.snackbarOpener = snackBar;
+ this.mediator = new Mediator(
+ this.tracePipeline,
+ this.timelineData,
+ this.abtChromeExtensionProtocol,
+ this.crossToolProtocol,
+ this,
+ this.snackbarOpener,
+ localStorage
+ );
+
+ const storeDarkMode = this.store.get('dark-mode');
+ const prefersDarkQuery = window.matchMedia?.('(prefers-color-scheme: dark)');
+ this.setDarkMode(storeDarkMode ? storeDarkMode === 'true' : prefersDarkQuery.matches);
+
+ if (!customElements.get('viewer-input-method')) {
+ customElements.define(
+ 'viewer-input-method',
+ createCustomElement(ViewerInputMethodComponent, {injector})
+ );
+ }
+ if (!customElements.get('viewer-protolog')) {
+ customElements.define(
+ 'viewer-protolog',
+ createCustomElement(ViewerProtologComponent, {injector})
+ );
+ }
+ if (!customElements.get('viewer-screen-recording')) {
+ customElements.define(
+ 'viewer-screen-recording',
+ createCustomElement(ViewerScreenRecordingComponent, {injector})
+ );
+ }
+ if (!customElements.get('viewer-surface-flinger')) {
+ customElements.define(
+ 'viewer-surface-flinger',
+ createCustomElement(ViewerSurfaceFlingerComponent, {injector})
+ );
+ }
+ if (!customElements.get('viewer-transactions')) {
+ customElements.define(
+ 'viewer-transactions',
+ createCustomElement(ViewerTransactionsComponent, {injector})
+ );
+ }
+ if (!customElements.get('viewer-window-manager')) {
+ customElements.define(
+ 'viewer-window-manager',
+ createCustomElement(ViewerWindowManagerComponent, {injector})
+ );
+ }
+ if (!customElements.get('viewer-transitions')) {
+ customElements.define(
+ 'viewer-transitions',
+ createCustomElement(ViewerTransitionsComponent, {injector})
+ );
+ }
+ }
+
+ ngAfterViewInit() {
+ this.mediator.onWinscopeInitialized();
+ }
+
+ ngAfterViewChecked() {
+ this.mediator.setUploadTracesComponent(this.uploadTracesComponent);
+ this.mediator.setCollectTracesComponent(this.collectTracesComponent);
+ this.mediator.setTimelineComponent(this.timelineComponent);
+ }
+
+ onCollapsedTimelineSizeChanged(height: number) {
+ this.collapsedTimelineHeight = height;
+ this.changeDetectorRef.detectChanges();
+ }
+
+ getLoadedTraceTypes(): TraceType[] {
+ return this.tracePipeline.getLoadedTraces().map((trace) => trace.type);
+ }
+
+ onTraceDataLoaded(viewers: Viewer[]) {
+ this.viewers = viewers;
+ this.dataLoaded = true;
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onTraceDataUnloaded() {
+ proxyClient.adbData = [];
+ this.dataLoaded = false;
+ this.changeDetectorRef.detectChanges();
+ }
+
+ setDarkMode(enabled: boolean) {
+ document.body.classList.toggle('dark-mode', enabled);
+ this.store.add('dark-mode', `${enabled}`);
+ this.isDarkModeOn = enabled;
+ }
+
+ async onDownloadTracesButtonClick() {
+ const traceFiles = await this.makeTraceFilesForDownload();
+ const zipFileBlob = await FileUtils.createZipArchive(traceFiles);
+ const zipFileName = 'winscope.zip';
+
+ const a = document.createElement('a');
+ document.body.appendChild(a);
+ const url = window.URL.createObjectURL(zipFileBlob);
+ a.href = url;
+ a.download = zipFileName;
+ a.click();
+ window.URL.revokeObjectURL(url);
+ document.body.removeChild(a);
+ }
+
+ onActiveViewChanged(view: View) {
+ this.activeView = view;
+ this.activeTrace = this.getActiveTrace(view);
+ this.activeTraceFileInfo = this.makeActiveTraceFileInfo(view);
+ this.timelineData.setActiveViewTraceTypes(view.dependencies);
+ }
+
+ goToLink(url: string) {
+ window.open(url, '_blank');
+ }
+
+ private makeActiveTraceFileInfo(view: View): string {
+ const trace = this.getActiveTrace(view);
+
+ if (!trace) {
+ return '';
+ }
+
+ return `${trace.descriptors.join(', ')}`;
+ }
+
+ private getActiveTrace(view: View): LoadedTrace | undefined {
+ return this.tracePipeline
+ .getLoadedTraces()
+ .find((trace) => trace.type === view.dependencies[0]);
+ }
+
+ private async makeTraceFilesForDownload(): Promise<File[]> {
+ const loadedFiles = this.tracePipeline.getLoadedFiles();
+ return [...loadedFiles.keys()].map((traceType) => {
+ const file = loadedFiles.get(traceType)!;
+ const path = TRACE_INFO[traceType].downloadArchiveDir;
+
+ const newName = path + '/' + FileUtils.removeDirFromFileName(file.file.name);
+ return new File([file.file], newName);
+ });
+ }
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/app/components/app_component_stub.ts
similarity index 62%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/app/components/app_component_stub.ts
index bfe19ce..2e84e8d 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/app/components/app_component_stub.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {TraceDataListener} from 'interfaces/trace_data_listener';
+import {Viewer} from 'viewers/viewer';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class AppComponentStub implements TraceDataListener {
+ onTraceDataLoaded(viewers: Viewer[]) {
+ // do nothing
+ }
+
+ onTraceDataUnloaded() {
+ // do nothing
+ }
+}
diff --git a/tools/winscope/src/app/components/app_component_test.ts b/tools/winscope/src/app/components/app_component_test.ts
new file mode 100644
index 0000000..937bf1c
--- /dev/null
+++ b/tools/winscope/src/app/components/app_component_test.ts
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {ChangeDetectionStrategy} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {MatButtonModule} from '@angular/material/button';
+import {MatCardModule} from '@angular/material/card';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatIconModule} from '@angular/material/icon';
+import {MatSelectModule} from '@angular/material/select';
+import {MatSliderModule} from '@angular/material/slider';
+import {MatSnackBarModule} from '@angular/material/snack-bar';
+import {MatToolbarModule} from '@angular/material/toolbar';
+import {MatTooltipModule} from '@angular/material/tooltip';
+
+import {ViewerSurfaceFlingerComponent} from 'viewers/viewer_surface_flinger/viewer_surface_flinger_component';
+import {AdbProxyComponent} from './adb_proxy_component';
+import {AppComponent} from './app_component';
+import {MatDrawer, MatDrawerContainer, MatDrawerContent} from './bottomnav/bottom_drawer_component';
+import {CollectTracesComponent} from './collect_traces_component';
+import {MiniTimelineComponent} from './timeline/mini_timeline_component';
+import {TimelineComponent} from './timeline/timeline_component';
+import {TraceConfigComponent} from './trace_config_component';
+import {TraceViewComponent} from './trace_view_component';
+import {UploadTracesComponent} from './upload_traces_component';
+import {WebAdbComponent} from './web_adb_component';
+
+describe('AppComponent', () => {
+ let fixture: ComponentFixture<AppComponent>;
+ let component: AppComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [
+ CommonModule,
+ FormsModule,
+ MatCardModule,
+ MatButtonModule,
+ MatDividerModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatSelectModule,
+ MatSliderModule,
+ MatSnackBarModule,
+ MatToolbarModule,
+ MatTooltipModule,
+ ReactiveFormsModule,
+ ],
+ declarations: [
+ AdbProxyComponent,
+ AppComponent,
+ CollectTracesComponent,
+ MatDrawer,
+ MatDrawerContainer,
+ MatDrawerContent,
+ MiniTimelineComponent,
+ TimelineComponent,
+ TraceConfigComponent,
+ TraceViewComponent,
+ UploadTracesComponent,
+ ViewerSurfaceFlingerComponent,
+ WebAdbComponent,
+ ],
+ })
+ .overrideComponent(AppComponent, {
+ set: {changeDetection: ChangeDetectionStrategy.Default},
+ })
+ .compileComponents();
+ fixture = TestBed.createComponent(AppComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('has the expected title', () => {
+ expect(component.title).toEqual('winscope');
+ });
+
+ it('renders the page title', () => {
+ expect(htmlElement.querySelector('.app-title')?.innerHTML).toContain('Winscope');
+ });
+
+ it('displays correct elements when no data loaded', () => {
+ component.dataLoaded = false;
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.welcome-info')).toBeTruthy();
+ expect(htmlElement.querySelector('.active-trace-file-info')).toBeFalsy();
+ expect(htmlElement.querySelector('.collect-traces-card')).toBeTruthy();
+ expect(htmlElement.querySelector('.upload-traces-card')).toBeTruthy();
+ expect(htmlElement.querySelector('.viewers')).toBeFalsy();
+ });
+
+ it('displays correct elements when data loaded', () => {
+ component.dataLoaded = true;
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.welcome-info')).toBeFalsy();
+ expect(htmlElement.querySelector('.active-trace-file-info')).toBeTruthy();
+ expect(htmlElement.querySelector('.collect-traces-card')).toBeFalsy();
+ expect(htmlElement.querySelector('.upload-traces-card')).toBeFalsy();
+ expect(htmlElement.querySelector('.viewers')).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/app/components/bottomnav/bottom_drawer_component.ts b/tools/winscope/src/app/components/bottomnav/bottom_drawer_component.ts
new file mode 100644
index 0000000..de0bf06
--- /dev/null
+++ b/tools/winscope/src/app/components/bottomnav/bottom_drawer_component.ts
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2022 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 {
+ animate,
+ AnimationTriggerMetadata,
+ state,
+ style,
+ transition,
+ trigger,
+} from '@angular/animations';
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ContentChild,
+ ElementRef,
+ forwardRef,
+ Inject,
+ Injectable,
+ Input,
+ NgZone,
+ ViewChild,
+ ViewEncapsulation,
+} from '@angular/core';
+import {Subject} from 'rxjs';
+import {debounceTime, takeUntil} from 'rxjs/operators';
+
+/**
+ * Animations used by the Material drawers.
+ * @docs-private
+ */
+export const matDrawerAnimations: {
+ readonly transformDrawer: AnimationTriggerMetadata;
+} = {
+ /** Animation that slides a drawer in and out. */
+ transformDrawer: trigger('transform', [
+ // We remove the `transform` here completely, rather than setting it to zero, because:
+ // 1. Having a transform can cause elements with ripples or an animated
+ // transform to shift around in Chrome with an RTL layout (see #10023).
+ // 2. 3d transforms causes text to appear blurry on IE and Edge.
+ state(
+ 'open, open-instant',
+ style({
+ transform: 'none',
+ visibility: 'visible',
+ })
+ ),
+ state(
+ 'void',
+ style({
+ // Avoids the shadow showing up when closed in SSR.
+ 'box-shadow': 'none',
+ visibility: 'hidden',
+ })
+ ),
+ transition('void => open-instant', animate('0ms')),
+ transition(
+ 'void <=> open, open-instant => void',
+ animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)')
+ ),
+ ]),
+};
+
+/**
+ * This component corresponds to a drawer that can be opened on the drawer container.
+ */
+@Injectable()
+@Component({
+ selector: 'mat-drawer',
+ exportAs: 'matDrawer',
+ template: `
+ <div class="mat-drawer-inner-container" #content>
+ <ng-content></ng-content>
+ </div>
+ `,
+ styles: [
+ `
+ .mat-drawer.mat-drawer-bottom {
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: unset;
+ position: fixed;
+ z-index: 5;
+ background-color: #f8f9fa;
+ box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px 1px rgba(0, 0, 0, 0.15);
+ }
+ `,
+ ],
+ animations: [matDrawerAnimations.transformDrawer],
+ host: {
+ class: 'mat-drawer mat-drawer-bottom',
+ // must prevent the browser from aligning text based on value
+ '[attr.align]': 'null',
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ encapsulation: ViewEncapsulation.None,
+})
+export class MatDrawer {
+ @Input() mode: 'push' | 'overlay' = 'overlay';
+ @Input() baseHeight = 0;
+
+ getBaseHeight() {
+ return this.baseHeight;
+ }
+}
+
+@Component({
+ selector: 'mat-drawer-content',
+ template: '<ng-content></ng-content>',
+ styles: [
+ `
+ .mat-drawer-content {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ z-index: 1;
+ height: unset;
+ overflow: unset;
+ width: 100%;
+ flex-grow: 1;
+ }
+ `,
+ ],
+ host: {
+ class: 'mat-drawer-content',
+ '[style.margin-top.px]': 'contentMargins.top',
+ '[style.margin-bottom.px]': 'contentMargins.bottom',
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ encapsulation: ViewEncapsulation.None,
+})
+export class MatDrawerContent /*extends MatDrawerContentBase*/ {
+ private contentMargins: {top: number | null; bottom: number | null} = {top: null, bottom: null};
+
+ constructor(
+ @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef,
+ @Inject(forwardRef(() => MatDrawerContainer)) public container: MatDrawerContainer
+ ) {}
+
+ ngAfterContentInit() {
+ this.container.contentMarginChanges.subscribe(() => {
+ this.changeDetectorRef.markForCheck();
+ });
+ }
+
+ setMargins(margins: {top: number | null; bottom: number | null}) {
+ this.contentMargins = margins;
+ }
+}
+
+@Component({
+ selector: 'mat-drawer-container',
+ exportAs: 'matDrawerContainer',
+ template: `
+ <ng-content select="mat-drawer-content"> </ng-content>
+
+ <ng-content select="mat-drawer"></ng-content>
+ `,
+ styles: [
+ `
+ .mat-drawer-container {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ align-items: center;
+ align-content: center;
+ justify-content: center;
+ }
+ `,
+ ],
+ host: {
+ class: 'mat-drawer-container',
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ encapsulation: ViewEncapsulation.None,
+})
+@Injectable()
+export class MatDrawerContainer /*extends MatDrawerContainerBase*/ {
+ /** Drawer that belong to this container. */
+ @ContentChild(MatDrawer) drawer!: MatDrawer;
+ @ContentChild(MatDrawer, {read: ElementRef}) drawerView!: ElementRef;
+
+ @ContentChild(MatDrawerContent) content!: MatDrawerContent;
+ @ViewChild(MatDrawerContent) userContent!: MatDrawerContent;
+
+ /**
+ * Margins to be applied to the content. These are used to push / shrink the drawer content when a
+ * drawer is open. We use margin rather than transform even for push mode because transform breaks
+ * fixed position elements inside of the transformed element.
+ */
+ contentMargins: {top: number | null; bottom: number | null} = {top: null, bottom: null};
+
+ readonly contentMarginChanges = new Subject<{top: number | null; bottom: number | null}>();
+
+ /** Emits on every ngDoCheck. Used for debouncing reflows. */
+ private readonly doCheckSubject = new Subject<void>();
+
+ /** Emits when the component is destroyed. */
+ private readonly destroyed = new Subject<void>();
+
+ constructor(@Inject(NgZone) private ngZone: NgZone) {}
+
+ ngAfterContentInit() {
+ this.updateContentMargins();
+
+ // Avoid hitting the NgZone through the debounce timeout.
+ this.ngZone.runOutsideAngular(() => {
+ this.doCheckSubject
+ .pipe(
+ debounceTime(10), // Arbitrary debounce time, less than a frame at 60fps
+ takeUntil(this.destroyed)
+ )
+ .subscribe(() => this.updateContentMargins());
+ });
+ }
+
+ ngOnDestroy() {
+ this.doCheckSubject.complete();
+ this.destroyed.next();
+ this.destroyed.complete();
+ }
+
+ ngDoCheck() {
+ this.ngZone.runOutsideAngular(() => this.doCheckSubject.next());
+ }
+
+ /**
+ * Recalculates and updates the inline styles for the content. Note that this should be used
+ * sparingly, because it causes a reflow.
+ */
+ updateContentMargins() {
+ // If shift is enabled want to shift the content without resizing it. We do
+ // this by adding to the top or bottom margin and simultaneously subtracting
+ // the same amount of margin from the other side.
+ let top = 0;
+ let bottom = 0;
+
+ const baseHeight = this.drawer.getBaseHeight();
+ const height = this.getDrawerHeight();
+ const shiftAmount = this.drawer.mode === 'push' ? Math.max(0, height - baseHeight) : 0;
+
+ top -= shiftAmount;
+ bottom += baseHeight + shiftAmount;
+
+ // If either `top` or `bottom` is zero, don't set a style to the element. This
+ // allows users to specify a custom size via CSS class in SSR scenarios where the
+ // measured widths will always be zero. Note that we reset to `null` here, rather
+ // than below, in order to ensure that the types in the `if` below are consistent.
+ top = top || null!;
+ bottom = bottom || null!;
+
+ if (top !== this.contentMargins.top || bottom !== this.contentMargins.bottom) {
+ this.contentMargins = {top, bottom};
+
+ this.content.setMargins(this.contentMargins);
+
+ // Pull back into the NgZone since in some cases we could be outside. We need to be careful
+ // to do it only when something changed, otherwise we can end up hitting the zone too often.
+ this.ngZone.run(() => this.contentMarginChanges.next(this.contentMargins));
+ }
+ }
+
+ getDrawerHeight(): number {
+ return this.drawerView.nativeElement ? this.drawerView.nativeElement.offsetHeight || 0 : 0;
+ }
+}
diff --git a/tools/winscope/src/app/components/bottomnav/drawer.scss b/tools/winscope/src/app/components/bottomnav/drawer.scss
new file mode 100644
index 0000000..783a0e0
--- /dev/null
+++ b/tools/winscope/src/app/components/bottomnav/drawer.scss
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2022 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 '@angular/cdk';
+
+$drawer-content-z-index: 1;
+$drawer-side-drawer-z-index: 2;
+$drawer-backdrop-z-index: 3;
+$drawer-over-drawer-z-index: 4;
+
+// Mixin that creates a new stacking context.
+// see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
+@mixin drawer-stacking-context($z-index: 1) {
+ position: relative;
+
+ // Use a z-index to create a new stacking context. (We can't use transform because it breaks fixed
+ // positioning inside of the transformed element).
+ z-index: $z-index;
+}
+
+.mat-drawer-container {
+ // We need a stacking context here so that the backdrop and drawers are clipped to the
+ // MatDrawerContainer. This creates a new z-index stack so we use low numbered z-indices.
+ // We create another stacking context in the '.mat-drawer-content' and in each drawer so that
+ // the application content does not get messed up with our own CSS.
+ @include drawer-stacking-context();
+
+ box-sizing: border-box;
+ -webkit-overflow-scrolling: touch;
+
+ // Need this to take up space in the layout.
+ display: block;
+
+ // Hide the drawers when they're closed.
+ overflow: hidden;
+
+ // TODO(hansl): Update this with a more robust solution.
+ &[fullscreen] {
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ position: absolute;
+
+ &.mat-drawer-container-has-open {
+ overflow: hidden;
+ }
+ }
+
+ // When the consumer explicitly enabled the backdrop,
+ // we have to pull the side drawers above it.
+ &.mat-drawer-container-explicit-backdrop .mat-drawer-side {
+ z-index: $drawer-backdrop-z-index;
+ }
+
+ // Note that the `NoopAnimationsModule` is being handled inside of the component code.
+ &.ng-animate-disabled, .ng-animate-disabled & {
+ .mat-drawer-backdrop, .mat-drawer-content {
+ transition: none;
+ }
+ }
+}
+
+.mat-drawer-backdrop {
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ position: absolute;
+
+ display: block;
+
+ // Because of the new stacking context, the z-index stack is new and we can use our own
+ // numbers.
+ z-index: $drawer-backdrop-z-index;
+
+ // We use 'visibility: hidden | visible' because 'display: none' will not animate any
+ // transitions, while visibility will interpolate transitions properly.
+ // see https://developer.mozilla.org/en-US/docs/Web/CSS/visibility, the Interpolation
+ // section.
+ visibility: hidden;
+
+ &.mat-drawer-shown {
+ visibility: visible;
+ }
+
+ .mat-drawer-transition & {
+ transition: {
+ duration: 400ms;
+ timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+ property: background-color, visibility;
+ }
+ }
+
+ @include cdk.high-contrast(active, off) {
+ opacity: 0.5;
+ }
+}
+
+.mat-drawer-content {
+ @include drawer-stacking-context($drawer-content-z-index);
+
+ display: block;
+ height: 100%;
+ overflow: auto;
+
+ .mat-drawer-transition & {
+ transition: {
+ duration: 400ms;
+ timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+ property: transform, margin-left, margin-right;
+ }
+ }
+}
+
+.mat-drawer {
+ $high-contrast-border: solid 1px currentColor;
+
+ @include drawer-stacking-context($drawer-over-drawer-z-index);
+
+ display: block;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ z-index: 3;
+ outline: 0;
+ box-sizing: border-box;
+ overflow-y: auto; // TODO(kara): revisit scrolling behavior for drawers
+ transform: translate3d(-100%, 0, 0);
+
+ &, [dir='rtl'] &.mat-drawer-end {
+ @include cdk.high-contrast(active, off) {
+ border-right: $high-contrast-border;
+ }
+ }
+
+ [dir='rtl'] &, &.mat-drawer-end {
+ @include cdk.high-contrast(active, off) {
+ border-left: $high-contrast-border;
+ border-right: none;
+ }
+ }
+
+ &.mat-drawer-side {
+ z-index: $drawer-side-drawer-z-index;
+ }
+
+ &.mat-drawer-end {
+ right: 0;
+ transform: translate3d(100%, 0, 0);
+ }
+
+ [dir='rtl'] & {
+ transform: translate3d(100%, 0, 0);
+
+ &.mat-drawer-end {
+ left: 0;
+ right: auto;
+ transform: translate3d(-100%, 0, 0);
+ }
+ }
+
+ // Usually the `visibility: hidden` added by the animation is enough to prevent focus from
+ // entering the hidden drawer content, but children with their own `visibility` can override it.
+ // This is a fallback that completely hides the content when the element becomes hidden.
+ // Note that we can't do this in the animation definition, because the style gets recomputed too
+ // late, breaking the animation because Angular didn't have time to figure out the target
+ // transform. This can also be achieved with JS, but it has issues when starting an
+ // animation before the previous one has finished.
+ &[style*='visibility: hidden'] {
+ display: none;
+ }
+}
+
+// Note that this div isn't strictly necessary on all browsers, however we need it in
+// order to avoid a layout issue in Chrome. The issue is that in RTL mode the browser doesn't
+// account for the sidenav's scrollbar while positioning, which ends up pushing it partially
+// out of the screen. We work around the issue by having the scrollbar be on this inner container.
+.mat-drawer-inner-container {
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.mat-sidenav-fixed {
+ position: fixed;
+}
\ No newline at end of file
diff --git a/tools/winscope/src/app/components/canvas/canvas_drawer.ts b/tools/winscope/src/app/components/canvas/canvas_drawer.ts
new file mode 100644
index 0000000..03e8e83
--- /dev/null
+++ b/tools/winscope/src/app/components/canvas/canvas_drawer.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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 {CanvasMouseHandler} from './canvas_mouse_handler';
+
+export interface Padding {
+ left: number;
+ top: number;
+ right: number;
+ bottom: number;
+}
+
+export interface CanvasDrawer {
+ draw(): void;
+ handler: CanvasMouseHandler;
+ canvas: HTMLCanvasElement;
+ ctx: CanvasRenderingContext2D;
+ padding: Padding;
+ getXScale(): number;
+ getYScale(): number;
+ getWidth(): number;
+ getHeight(): number;
+}
diff --git a/tools/winscope/src/app/components/canvas/canvas_mouse_handler.ts b/tools/winscope/src/app/components/canvas/canvas_mouse_handler.ts
new file mode 100644
index 0000000..4eb3bea
--- /dev/null
+++ b/tools/winscope/src/app/components/canvas/canvas_mouse_handler.ts
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 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 {CanvasDrawer} from './canvas_drawer';
+import {DraggableCanvasObject} from './draggable_canvas_object';
+
+export type DragListener = (x: number, y: number) => void;
+export type DropListener = DragListener;
+
+export class CanvasMouseHandler {
+ // Ordered top most element to bottom most
+ private draggableObjects: DraggableCanvasObject[] = [];
+ private draggingObject: DraggableCanvasObject | undefined = undefined;
+
+ private onDrag = new Map<DraggableCanvasObject, DragListener>();
+ private onDrop = new Map<DraggableCanvasObject, DropListener>();
+
+ constructor(
+ private drawer: CanvasDrawer,
+ private defaultCursor: string = 'auto',
+ private onUnhandledMouseDown: (x: number, y: number) => void = (x, y) => {}
+ ) {
+ this.drawer.canvas.addEventListener('mousemove', (event) => {
+ this.handleMouseMove(event);
+ });
+ this.drawer.canvas.addEventListener('mousedown', (event) => {
+ this.handleMouseDown(event);
+ });
+ this.drawer.canvas.addEventListener('mouseup', (event) => {
+ this.handleMouseUp(event);
+ });
+ this.drawer.canvas.addEventListener('mouseout', (event) => {
+ this.handleMouseUp(event);
+ });
+ }
+
+ registerDraggableObject(
+ draggableObject: DraggableCanvasObject,
+ onDrag: DragListener,
+ onDrop: DropListener
+ ) {
+ this.onDrag.set(draggableObject, onDrag);
+ this.onDrop.set(draggableObject, onDrop);
+ }
+
+ notifyDrawnOnTop(draggableObject: DraggableCanvasObject) {
+ const foundIndex = this.draggableObjects.indexOf(draggableObject);
+ if (foundIndex !== -1) {
+ this.draggableObjects.splice(foundIndex, 1);
+ }
+ this.draggableObjects.unshift(draggableObject);
+ }
+
+ private handleMouseDown(e: MouseEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ const {mouseX, mouseY} = this.getPos(e);
+
+ const clickedObject = this.objectAt(mouseX, mouseY);
+ if (clickedObject !== undefined) {
+ this.draggingObject = clickedObject;
+ } else {
+ this.onUnhandledMouseDown(mouseX, mouseY);
+ }
+ this.updateCursor(mouseX, mouseY);
+ }
+
+ private handleMouseMove(e: MouseEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ const {mouseX, mouseY} = this.getPos(e);
+
+ if (this.draggingObject !== undefined) {
+ const onDragCallback = this.onDrag.get(this.draggingObject);
+ if (onDragCallback !== undefined) {
+ onDragCallback(mouseX, mouseY);
+ }
+ }
+
+ this.updateCursor(mouseX, mouseY);
+ }
+
+ private handleMouseUp(e: MouseEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ const {mouseX, mouseY} = this.getPos(e);
+
+ if (this.draggingObject !== undefined) {
+ const onDropCallback = this.onDrop.get(this.draggingObject);
+ if (onDropCallback !== undefined) {
+ onDropCallback(mouseX, mouseY);
+ }
+ }
+
+ this.draggingObject = undefined;
+ this.updateCursor(mouseX, mouseY);
+ }
+
+ private getPos(e: MouseEvent) {
+ let mouseX = e.offsetX;
+ const mouseY = e.offsetY;
+
+ if (mouseX < this.drawer.padding.left) {
+ mouseX = this.drawer.padding.left;
+ }
+
+ if (mouseX > this.drawer.getWidth() - this.drawer.padding.right) {
+ mouseX = this.drawer.getWidth() - this.drawer.padding.right;
+ }
+
+ return {mouseX, mouseY};
+ }
+
+ private updateCursor(mouseX: number, mouseY: number) {
+ const hoverObject = this.objectAt(mouseX, mouseY);
+ if (hoverObject !== undefined) {
+ if (hoverObject === this.draggingObject) {
+ this.drawer.canvas.style.cursor = 'grabbing';
+ } else {
+ this.drawer.canvas.style.cursor = 'grab';
+ }
+ } else {
+ this.drawer.canvas.style.cursor = this.defaultCursor;
+ }
+ }
+
+ private objectAt(mouseX: number, mouseY: number): DraggableCanvasObject | undefined {
+ for (const object of this.draggableObjects) {
+ object.definePath(this.drawer.ctx);
+ if (
+ this.drawer.ctx.isPointInPath(
+ mouseX * this.drawer.getXScale(),
+ mouseY * this.drawer.getYScale()
+ )
+ ) {
+ return object;
+ }
+ }
+
+ return undefined;
+ }
+}
diff --git a/tools/winscope/src/app/components/canvas/draggable_canvas_object.ts b/tools/winscope/src/app/components/canvas/draggable_canvas_object.ts
new file mode 100644
index 0000000..65b1032
--- /dev/null
+++ b/tools/winscope/src/app/components/canvas/draggable_canvas_object.ts
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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 {MathUtils} from 'three/src/Three';
+import {Segment} from '../timeline/utils';
+import {CanvasDrawer} from './canvas_drawer';
+
+export interface DrawConfig {
+ fillStyle: string;
+ fill: boolean;
+}
+
+export class DraggableCanvasObject {
+ private draggingPosition: number | undefined;
+
+ constructor(
+ private drawer: CanvasDrawer,
+ private positionGetter: () => number,
+ private definePathFunc: (ctx: CanvasRenderingContext2D, position: number) => void,
+ private drawConfig: DrawConfig,
+ private onDrag: (x: number) => void,
+ private onDrop: (x: number) => void,
+ private rangeGetter: () => Segment
+ ) {
+ this.drawer.handler.registerDraggableObject(
+ this,
+ (x: number) => {
+ this.draggingPosition = this.clampPositionToRange(x);
+ this.onDrag(this.draggingPosition);
+ this.drawer.draw();
+ },
+ (x: number) => {
+ this.draggingPosition = undefined;
+ this.onDrop(this.clampPositionToRange(x));
+ this.drawer.draw();
+ }
+ );
+ }
+
+ get range(): Segment {
+ return this.rangeGetter();
+ }
+
+ get position(): number {
+ return this.draggingPosition !== undefined ? this.draggingPosition : this.positionGetter();
+ }
+
+ definePath(ctx: CanvasRenderingContext2D) {
+ this.definePathFunc(ctx, this.position);
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ this.doDraw(ctx);
+ this.drawer.handler.notifyDrawnOnTop(this);
+ }
+
+ private doDraw(ctx: CanvasRenderingContext2D) {
+ this.definePath(ctx);
+ ctx.fillStyle = this.drawConfig.fillStyle;
+ if (this.drawConfig.fill) {
+ ctx.fill();
+ }
+ }
+
+ private clampPositionToRange(x: number): number {
+ return MathUtils.clamp(x, this.range.from, this.range.to);
+ }
+}
diff --git a/tools/winscope/src/app/components/collect_traces_component.ts b/tools/winscope/src/app/components/collect_traces_component.ts
new file mode 100644
index 0000000..edcc2e4
--- /dev/null
+++ b/tools/winscope/src/app/components/collect_traces_component.ts
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2022 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 {
+ ChangeDetectorRef,
+ Component,
+ EventEmitter,
+ Inject,
+ Input,
+ NgZone,
+ OnDestroy,
+ OnInit,
+ Output,
+ ViewEncapsulation,
+} from '@angular/core';
+import {PersistentStore} from 'common/persistent_store';
+import {ProgressListener} from 'interfaces/progress_listener';
+import {Connection} from 'trace_collection/connection';
+import {ProxyState} from 'trace_collection/proxy_client';
+import {ProxyConnection} from 'trace_collection/proxy_connection';
+import {
+ ConfigMap,
+ EnableConfiguration,
+ SelectionConfiguration,
+ traceConfigurations,
+} from 'trace_collection/trace_collection_utils';
+import {TracingConfig} from 'trace_collection/tracing_config';
+import {LoadProgressComponent} from './load_progress_component';
+
+@Component({
+ selector: 'collect-traces',
+ template: `
+ <mat-card class="collect-card">
+ <mat-card-title class="title">Collect Traces</mat-card-title>
+
+ <mat-card-content class="collect-card-content">
+ <p *ngIf="connect.isConnectingState()" class="connecting-message mat-body-1">
+ Connecting...
+ </p>
+
+ <div *ngIf="!connect.adbSuccess()" class="set-up-adb">
+ <button
+ class="proxy-tab"
+ color="primary"
+ mat-stroked-button
+ [ngClass]="tabClass(true)"
+ (click)="displayAdbProxyTab()">
+ ADB Proxy
+ </button>
+ <!-- <button class="web-tab" color="primary" mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button> -->
+ <adb-proxy
+ *ngIf="isAdbProxy"
+ [(proxy)]="connect.proxy!"
+ (addKey)="onAddKey($event)"></adb-proxy>
+ <!-- <web-adb *ngIf="!isAdbProxy"></web-adb> TODO: fix web adb workflow -->
+ </div>
+
+ <div *ngIf="connect.isDevicesState()" class="devices-connecting">
+ <div *ngIf="objectKeys(connect.devices()).length === 0" class="no-device-detected">
+ <p class="mat-body-3 icon"><mat-icon inline fontIcon="phonelink_erase"></mat-icon></p>
+ <p class="mat-body-1">No devices detected</p>
+ </div>
+ <div *ngIf="objectKeys(connect.devices()).length > 0" class="device-selection">
+ <p class="mat-body-1 instruction">Select a device:</p>
+ <mat-list *ngIf="objectKeys(connect.devices()).length > 0">
+ <mat-list-item
+ *ngFor="let deviceId of objectKeys(connect.devices())"
+ (click)="connect.selectDevice(deviceId)"
+ class="available-device">
+ <mat-icon matListIcon>
+ {{
+ connect.devices()[deviceId].authorised ? 'smartphone' : 'screen_lock_portrait'
+ }}
+ </mat-icon>
+ <p matLine>
+ {{
+ connect.devices()[deviceId].authorised
+ ? connect.devices()[deviceId]?.model
+ : 'unauthorised'
+ }}
+ ({{ deviceId }})
+ </p>
+ </mat-list-item>
+ </mat-list>
+ </div>
+ </div>
+
+ <div
+ *ngIf="
+ connect.isStartTraceState() || connect.isEndTraceState() || isOperationInProgress()
+ "
+ class="trace-collection-config">
+ <mat-list>
+ <mat-list-item>
+ <mat-icon matListIcon>smartphone</mat-icon>
+ <p matLine>
+ {{ connect.selectedDevice()?.model }} ({{ connect.selectedDeviceId() }})
+
+ <button
+ color="primary"
+ class="change-btn"
+ mat-button
+ (click)="connect.resetLastDevice()"
+ [disabled]="connect.isEndTraceState() || isOperationInProgress()">
+ Change device
+ </button>
+ </p>
+ </mat-list-item>
+ </mat-list>
+
+ <mat-tab-group class="tracing-tabs">
+ <mat-tab
+ label="Trace"
+ [disabled]="connect.isEndTraceState() || isOperationInProgress()">
+ <div class="tabbed-section">
+ <div class="trace-section" *ngIf="connect.isStartTraceState()">
+ <trace-config></trace-config>
+ <div class="start-btn">
+ <button color="primary" mat-stroked-button (click)="startTracing()">
+ Start trace
+ </button>
+ </div>
+ </div>
+
+ <div *ngIf="connect.isEndTraceState()" class="end-tracing">
+ <div class="progress-desc">
+ <p class="mat-body-3"><mat-icon fontIcon="cable"></mat-icon></p>
+ <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+ <p class="mat-body-1">Tracing...</p>
+ </div>
+ <div class="end-btn">
+ <button color="primary" mat-raised-button (click)="endTrace()">
+ End trace
+ </button>
+ </div>
+ </div>
+
+ <div *ngIf="isOperationInProgress()" class="load-data">
+ <load-progress
+ [progressPercentage]="progressPercentage"
+ [message]="progressMessage">
+ </load-progress>
+ <div class="end-btn">
+ <button color="primary" mat-raised-button (click)="endTrace()" disabled="true">
+ End trace
+ </button>
+ </div>
+ </div>
+ </div>
+ </mat-tab>
+ <mat-tab label="Dump" [disabled]="connect.isEndTraceState() || isOperationInProgress()">
+ <div class="tabbed-section">
+ <div class="dump-section" *ngIf="connect.isStartTraceState()">
+ <h3 class="mat-subheading-2">Dump targets</h3>
+ <div class="selection">
+ <mat-checkbox
+ *ngFor="let dumpKey of objectKeys(tracingConfig.getDumpConfig())"
+ color="primary"
+ class="dump-checkbox"
+ [(ngModel)]="tracingConfig.getDumpConfig()[dumpKey].run"
+ >{{ tracingConfig.getDumpConfig()[dumpKey].name }}</mat-checkbox
+ >
+ </div>
+ <div class="dump-btn">
+ <button color="primary" mat-stroked-button (click)="dumpState()">
+ Dump state
+ </button>
+ </div>
+ </div>
+
+ <load-progress
+ *ngIf="isOperationInProgress()"
+ [progressPercentage]="progressPercentage"
+ [message]="progressMessage">
+ </load-progress>
+ </div>
+ </mat-tab>
+ </mat-tab-group>
+ </div>
+
+ <div *ngIf="connect.isErrorState()" class="unknown-error">
+ <p class="error-wrapper mat-body-1">
+ <mat-icon class="error-icon">error</mat-icon>
+ Error:
+ </p>
+ <pre> {{ connect.proxy?.errorText }} </pre>
+ <button color="primary" class="retry-btn" mat-raised-button (click)="connect.restart()">
+ Retry
+ </button>
+ </div>
+ </mat-card-content>
+ </mat-card>
+ `,
+ styles: [
+ `
+ .change-btn,
+ .retry-btn {
+ margin-left: 5px;
+ }
+ .mat-card.collect-card {
+ display: flex;
+ }
+ .collect-card {
+ height: 100%;
+ flex-direction: column;
+ overflow: auto;
+ margin: 10px;
+ }
+ .collect-card-content {
+ overflow: auto;
+ }
+ .selection {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ .set-up-adb,
+ .trace-collection-config,
+ .trace-section,
+ .dump-section,
+ .end-tracing,
+ .load-data,
+ trace-config {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+ .trace-section,
+ .dump-section,
+ .end-tracing,
+ .load-data {
+ height: 100%;
+ }
+ .trace-collection-config {
+ height: 100%;
+ }
+ .proxy-tab,
+ .web-tab,
+ .start-btn,
+ .dump-btn,
+ .end-btn {
+ align-self: flex-start;
+ }
+ .start-btn,
+ .dump-btn,
+ .end-btn {
+ margin: auto 0 0 0;
+ padding: 1rem 0 0 0;
+ }
+ .error-wrapper {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
+ .error-icon {
+ margin-right: 5px;
+ }
+ .available-device {
+ cursor: pointer;
+ }
+
+ .no-device-detected {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ height: 100%;
+ }
+
+ .no-device-detected p,
+ .device-selection p.instruction {
+ padding-top: 1rem;
+ opacity: 0.6;
+ font-size: 1.2rem;
+ }
+
+ .no-device-detected .icon {
+ font-size: 3rem;
+ margin: 0 0 0.2rem 0;
+ }
+
+ .devices-connecting {
+ height: 100%;
+ }
+
+ mat-card-content {
+ flex-grow: 1;
+ }
+
+ mat-tab-body {
+ padding: 1rem;
+ }
+
+ .loading-info {
+ opacity: 0.8;
+ padding: 1rem 0;
+ }
+
+ .tracing-tabs {
+ flex-grow: 1;
+ }
+
+ .tracing-tabs .mat-tab-body-wrapper {
+ flex-grow: 1;
+ }
+
+ .tabbed-section {
+ height: 100%;
+ }
+
+ .load-data p,
+ .end-tracing p {
+ opacity: 0.7;
+ }
+
+ .progress-desc {
+ display: flex;
+ height: 100%;
+ flex-direction: column;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ }
+
+ .progress-desc > * {
+ max-width: 250px;
+ }
+
+ load-progress {
+ height: 100%;
+ }
+ `,
+ ],
+ encapsulation: ViewEncapsulation.None,
+})
+export class CollectTracesComponent implements OnInit, OnDestroy, ProgressListener {
+ objectKeys = Object.keys;
+ isAdbProxy = true;
+ traceConfigurations = traceConfigurations;
+ connect: Connection;
+ tracingConfig = TracingConfig.getInstance();
+
+ isExternalOperationInProgress = false;
+ progressMessage = 'Fetching...';
+ progressPercentage: number | undefined;
+ lastUiProgressUpdateTimeMs?: number;
+
+ @Input() store!: PersistentStore;
+ @Output() filesCollected = new EventEmitter<File[]>();
+
+ constructor(
+ @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef,
+ @Inject(NgZone) private ngZone: NgZone
+ ) {
+ this.connect = new ProxyConnection(
+ (newState) => this.changeDetectorRef.detectChanges(),
+ (progress) => this.onLoadProgressUpdate(progress)
+ );
+ }
+
+ ngOnInit() {
+ if (this.isAdbProxy) {
+ this.connect = new ProxyConnection(
+ (newState) => this.changeDetectorRef.detectChanges(),
+ (progress) => this.onLoadProgressUpdate(progress)
+ );
+ } else {
+ // TODO: change to WebAdbConnection
+ this.connect = new ProxyConnection(
+ (newState) => this.changeDetectorRef.detectChanges(),
+ (progress) => this.onLoadProgressUpdate(progress)
+ );
+ }
+ }
+
+ ngOnDestroy(): void {
+ this.connect.proxy?.removeOnProxyChange(this.onProxyChange);
+ }
+
+ onProgressUpdate(message: string, progressPercentage: number | undefined) {
+ if (!LoadProgressComponent.canUpdateComponent(this.lastUiProgressUpdateTimeMs)) {
+ return;
+ }
+ this.isExternalOperationInProgress = true;
+ this.progressMessage = message;
+ this.progressPercentage = progressPercentage;
+ this.lastUiProgressUpdateTimeMs = Date.now();
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onOperationFinished() {
+ this.isExternalOperationInProgress = false;
+ this.lastUiProgressUpdateTimeMs = undefined;
+ this.changeDetectorRef.detectChanges();
+ }
+
+ isOperationInProgress(): boolean {
+ return this.connect.isLoadDataState() || this.isExternalOperationInProgress;
+ }
+
+ onAddKey(key: string) {
+ if (this.connect.setProxyKey) {
+ this.connect.setProxyKey(key);
+ }
+ this.connect.restart();
+ }
+
+ displayAdbProxyTab() {
+ this.isAdbProxy = true;
+ this.connect = new ProxyConnection(
+ (newState) => this.changeDetectorRef.detectChanges(),
+ (progress) => this.onLoadProgressUpdate(progress)
+ );
+ }
+
+ displayWebAdbTab() {
+ this.isAdbProxy = false;
+ //TODO: change to WebAdbConnection
+ this.connect = new ProxyConnection(
+ (newState) => this.changeDetectorRef.detectChanges(),
+ (progress) => this.onLoadProgressUpdate(progress)
+ );
+ }
+
+ startTracing() {
+ console.log('begin tracing');
+ this.tracingConfig.requestedTraces = this.requestedTraces();
+ const reqEnableConfig = this.requestedEnableConfig();
+ const reqSelectedSfConfig = this.requestedSelection('layers_trace');
+ const reqSelectedWmConfig = this.requestedSelection('window_trace');
+ if (this.tracingConfig.requestedTraces.length < 1) {
+ this.connect.throwNoTargetsError();
+ return;
+ }
+ this.connect.startTrace(reqEnableConfig, reqSelectedSfConfig, reqSelectedWmConfig);
+ }
+
+ async dumpState() {
+ console.log('begin dump');
+ this.tracingConfig.requestedDumps = this.requestedDumps();
+ const dumpSuccessful = await this.connect.dumpState();
+ if (dumpSuccessful) {
+ this.filesCollected.emit(this.connect.adbData());
+ }
+ }
+
+ async endTrace() {
+ console.log('end tracing');
+ await this.connect.endTrace();
+ this.filesCollected.emit(this.connect.adbData());
+ }
+
+ tabClass(adbTab: boolean) {
+ let isActive: string;
+ if (adbTab) {
+ isActive = this.isAdbProxy ? 'active' : 'inactive';
+ } else {
+ isActive = !this.isAdbProxy ? 'active' : 'inactive';
+ }
+ return ['tab', isActive];
+ }
+
+ private onProxyChange(newState: ProxyState) {
+ this.connect.onConnectChange.bind(this.connect)(newState);
+ }
+
+ private requestedTraces() {
+ const tracesFromCollection: string[] = [];
+ const tracingConfig = this.tracingConfig.getTraceConfig();
+ const req = Object.keys(tracingConfig).filter((traceKey: string) => {
+ const traceConfig = tracingConfig[traceKey];
+ if (traceConfig.isTraceCollection) {
+ traceConfig.config?.enableConfigs.forEach((innerTrace: EnableConfiguration) => {
+ if (innerTrace.enabled) {
+ tracesFromCollection.push(innerTrace.key);
+ }
+ });
+ return false;
+ }
+ return traceConfig.run;
+ });
+ return req.concat(tracesFromCollection);
+ }
+
+ private requestedDumps() {
+ const dumpConfig = this.tracingConfig.getDumpConfig();
+ return Object.keys(dumpConfig).filter((dumpKey: string) => {
+ return dumpConfig[dumpKey].run;
+ });
+ }
+
+ private requestedEnableConfig(): string[] {
+ const req: string[] = [];
+ const tracingConfig = this.tracingConfig.getTraceConfig();
+ Object.keys(tracingConfig).forEach((traceKey: string) => {
+ const trace = tracingConfig[traceKey];
+ if (!trace.isTraceCollection && trace.run && trace.config && trace.config.enableConfigs) {
+ trace.config.enableConfigs.forEach((con: EnableConfiguration) => {
+ if (con.enabled) {
+ req.push(con.key);
+ }
+ });
+ }
+ });
+ return req;
+ }
+
+ private requestedSelection(traceType: string): ConfigMap | undefined {
+ const tracingConfig = this.tracingConfig.getTraceConfig();
+ if (!tracingConfig[traceType].run) {
+ return undefined;
+ }
+ const selected: ConfigMap = {};
+ tracingConfig[traceType].config?.selectionConfigs.forEach((con: SelectionConfiguration) => {
+ selected[con.key] = con.value;
+ });
+ return selected;
+ }
+
+ private onLoadProgressUpdate(progressPercentage: number) {
+ this.progressPercentage = progressPercentage;
+ this.changeDetectorRef.detectChanges();
+ }
+}
diff --git a/tools/winscope/src/app/components/collect_traces_component_test.ts b/tools/winscope/src/app/components/collect_traces_component_test.ts
new file mode 100644
index 0000000..f0ee825
--- /dev/null
+++ b/tools/winscope/src/app/components/collect_traces_component_test.ts
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2022 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatButtonModule} from '@angular/material/button';
+import {MatCardModule} from '@angular/material/card';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatIconModule} from '@angular/material/icon';
+import {MatListModule} from '@angular/material/list';
+import {MatProgressBarModule} from '@angular/material/progress-bar';
+import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {AdbProxyComponent} from './adb_proxy_component';
+import {CollectTracesComponent} from './collect_traces_component';
+import {TraceConfigComponent} from './trace_config_component';
+import {WebAdbComponent} from './web_adb_component';
+
+describe('CollectTracesComponent', () => {
+ let fixture: ComponentFixture<CollectTracesComponent>;
+ let component: CollectTracesComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ MatIconModule,
+ MatCardModule,
+ MatListModule,
+ MatButtonModule,
+ MatDividerModule,
+ MatProgressBarModule,
+ BrowserAnimationsModule,
+ MatSnackBarModule,
+ ],
+ providers: [MatSnackBar],
+ declarations: [
+ CollectTracesComponent,
+ AdbProxyComponent,
+ WebAdbComponent,
+ TraceConfigComponent,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ fixture = TestBed.createComponent(CollectTracesComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ component.isAdbProxy = true;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('renders the expected card title', () => {
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.title')?.innerHTML).toContain('Collect Traces');
+ });
+
+ it('displays connecting message', () => {
+ component.connect.isConnectingState = jasmine.createSpy().and.returnValue(true);
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.connecting-message')?.innerHTML).toContain('Connecting...');
+ });
+
+ it('displays adb set up', async () => {
+ component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false);
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(htmlElement.querySelector('.set-up-adb')).toBeTruthy();
+ const proxyTab: HTMLButtonElement | null = htmlElement.querySelector('.proxy-tab');
+ expect(proxyTab).toBeInstanceOf(HTMLButtonElement);
+ const webTab: HTMLButtonElement | null = htmlElement.querySelector('.web-tab');
+ expect(webTab).toBeInstanceOf(HTMLButtonElement);
+ });
+ });
+
+ it('displays adb proxy element', async () => {
+ component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false);
+ component.isAdbProxy = true;
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(htmlElement.querySelector('adb-proxy')).toBeTruthy();
+ expect(htmlElement.querySelector('web-adb')).toBeFalsy();
+ });
+ });
+
+ it('displays web adb element', async () => {
+ component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false);
+ component.isAdbProxy = false;
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(htmlElement.querySelector('adb-proxy')).toBeFalsy();
+ expect(htmlElement.querySelector('web-adb')).toBeTruthy();
+ });
+ });
+
+ it('changes to adb workflow tab', async () => {
+ component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false);
+ component.isAdbProxy = true;
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const webTab: HTMLButtonElement | null = htmlElement.querySelector('.web-tab');
+ expect(webTab).toBeInstanceOf(HTMLButtonElement);
+ webTab?.dispatchEvent(new Event('click'));
+ fixture.whenStable().then(() => {
+ expect(component.displayWebAdbTab).toHaveBeenCalled();
+ });
+ });
+ });
+
+ it('displays no connected devices', async () => {
+ component.connect.isDevicesState = jasmine.createSpy().and.returnValue(true);
+ component.connect.devices = jasmine.createSpy().and.returnValue({});
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const el = htmlElement.querySelector('.devices-connecting');
+ expect(el).toBeTruthy();
+ expect(el?.innerHTML).toContain('No devices detected');
+ });
+ });
+
+ it('displays connected authorised devices', async () => {
+ component.connect.isDevicesState = jasmine.createSpy().and.returnValue(true);
+ component.connect.devices = jasmine
+ .createSpy()
+ .and.returnValue({'35562': {model: 'Pixel 6', authorised: true}});
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const el = htmlElement.querySelector('.devices-connecting');
+ expect(el).toBeTruthy();
+ expect(el?.innerHTML).toContain('Connected devices:');
+ expect(el?.innerHTML).toContain('Pixel 6');
+ expect(el?.innerHTML).toContain('smartphone');
+ });
+ });
+
+ it('displays connected unauthorised devices', async () => {
+ component.connect.isDevicesState = jasmine.createSpy().and.returnValue(true);
+ component.connect.devices = jasmine
+ .createSpy()
+ .and.returnValue({'35562': {model: 'Pixel 6', authorised: false}});
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const el = htmlElement.querySelector('.devices-connecting');
+ expect(el).toBeTruthy();
+ expect(el?.innerHTML).toContain('Connected devices:');
+ expect(el?.innerHTML).toContain('unauthorised');
+ expect(el?.innerHTML).toContain('screen_lock_portrait');
+ });
+ });
+
+ it('displays trace collection config elements', async () => {
+ component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true);
+ const mock = {model: 'Pixel 6', authorised: true};
+ component.connect.devices = jasmine.createSpy().and.returnValue({'35562': mock});
+ component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ const el = htmlElement.querySelector('.trace-collection-config');
+ expect(el).toBeTruthy();
+ expect(el?.innerHTML).toContain('smartphone');
+ expect(el?.innerHTML).toContain('Pixel 6');
+ expect(el?.innerHTML).toContain('35562');
+
+ const traceSection = htmlElement.querySelector('.trace-section');
+ expect(traceSection).toBeTruthy();
+
+ const dumpSection = htmlElement.querySelector('.dump-section');
+ expect(dumpSection).toBeTruthy();
+ });
+ });
+
+ it('start trace button works as expected', async () => {
+ component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true);
+ const mock = {model: 'Pixel 6', authorised: true};
+ component.connect.devices = jasmine.createSpy().and.returnValue({'35562': mock});
+ component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ const start: HTMLButtonElement | null = htmlElement.querySelector('.start-btn');
+ expect(start).toBeInstanceOf(HTMLButtonElement);
+ start?.dispatchEvent(new Event('click'));
+ fixture.whenStable().then(() => {
+ expect(component.startTracing).toHaveBeenCalled();
+ expect(component.connect.startTrace).toHaveBeenCalled();
+ });
+ });
+ });
+
+ it('dump state button works as expected', async () => {
+ component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true);
+ const mock = {model: 'Pixel 6', authorised: true};
+ component.connect.devices = jasmine.createSpy().and.returnValue({'35562': mock});
+ component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ const dump: HTMLButtonElement | null = htmlElement.querySelector('.dump-btn');
+ expect(dump).toBeInstanceOf(HTMLButtonElement);
+ dump?.dispatchEvent(new Event('click'));
+ fixture.whenStable().then(() => {
+ expect(component.dumpState).toHaveBeenCalled();
+ expect(component.connect.dumpState).toHaveBeenCalled();
+ });
+ });
+ });
+
+ it('change device button works as expected', async () => {
+ component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true);
+ const mock = {model: 'Pixel 6', authorised: true};
+ component.connect.devices = jasmine.createSpy().and.returnValue({'35562': mock});
+ component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ const change: HTMLButtonElement | null = htmlElement.querySelector('.change-btn');
+ expect(change).toBeInstanceOf(HTMLButtonElement);
+ change?.dispatchEvent(new Event('click'));
+ fixture.whenStable().then(() => {
+ expect(component.connect.resetLastDevice).toHaveBeenCalled();
+ });
+ });
+ });
+
+ it('displays unknown error message', () => {
+ component.connect.isErrorState = jasmine.createSpy().and.returnValue(true);
+ component.connect.proxy!.errorText = 'bad things are happening';
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const el = htmlElement.querySelector('.unknown-error');
+ expect(el?.innerHTML).toContain('Error:');
+ expect(el?.innerHTML).toContain('bad things are happening');
+ const retry: HTMLButtonElement | null = htmlElement.querySelector('.retry-btn');
+ expect(retry).toBeInstanceOf(HTMLButtonElement);
+ retry?.dispatchEvent(new Event('click'));
+ fixture.whenStable().then(() => {
+ expect(component.connect.restart).toHaveBeenCalled();
+ });
+ });
+ });
+
+ it('displays end tracing elements', () => {
+ component.connect.isEndTraceState = jasmine.createSpy().and.returnValue(true);
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const el = htmlElement.querySelector('.end-tracing');
+ expect(el?.innerHTML).toContain('Tracing...');
+ expect(htmlElement.querySelector('mat-progress-bar')).toBeTruthy();
+
+ const end: HTMLButtonElement | null = htmlElement.querySelector('.end');
+ expect(end).toBeInstanceOf(HTMLButtonElement);
+ end?.dispatchEvent(new Event('click'));
+ fixture.whenStable().then(() => {
+ expect(component.endTrace).toHaveBeenCalled();
+ expect(component.connect.endTrace).toHaveBeenCalled();
+ });
+ });
+ });
+
+ it('displays loading data elements', () => {
+ component.connect.isLoadDataState = jasmine.createSpy().and.returnValue(true);
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(htmlElement.querySelector('.load-data')?.innerHTML).toContain('Loading data...');
+ expect(htmlElement.querySelector('mat-progress-bar')).toBeTruthy();
+ });
+ });
+});
diff --git a/tools/winscope/src/app/components/load_progress_component.ts b/tools/winscope/src/app/components/load_progress_component.ts
new file mode 100644
index 0000000..2c17445
--- /dev/null
+++ b/tools/winscope/src/app/components/load_progress_component.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+
+@Component({
+ selector: 'load-progress',
+ template: `
+ <div class="container-progress">
+ <p class="mat-body-3">
+ <mat-icon fontIcon="sync"> </mat-icon>
+ </p>
+
+ <mat-progress-bar *ngIf="progressPercentage === undefined" mode="indeterminate">
+ </mat-progress-bar>
+ <mat-progress-bar
+ *ngIf="progressPercentage !== undefined"
+ mode="determinate"
+ [value]="progressPercentage">
+ </mat-progress-bar>
+
+ <p class="mat-body-1">{{ message }}</p>
+ </div>
+ `,
+ styles: [
+ `
+ .container-progress {
+ display: flex;
+ height: 100%;
+ flex-direction: column;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ }
+ p {
+ opacity: 0.6;
+ }
+ mat-icon {
+ font-size: 3rem;
+ width: unset;
+ height: unset;
+ }
+ mat-progress-bar {
+ max-width: 250px;
+ }
+ mat-card-content {
+ flex-grow: 1;
+ }
+ `,
+ ],
+})
+export class LoadProgressComponent {
+ @Input() progressPercentage?: number;
+ @Input() message = 'Loading...';
+ private static readonly MIN_UI_UPDATE_PERIOD_MS = 200;
+
+ static canUpdateComponent(lastUpdateTimeMs: number | undefined): boolean {
+ if (lastUpdateTimeMs === undefined) {
+ return true;
+ }
+ // Limit the amount of UI updates, because the progress bar component
+ // renders weird stuff when updated too frequently.
+ // Also, this way we save some resources.
+ return Date.now() - lastUpdateTimeMs >= LoadProgressComponent.MIN_UI_UPDATE_PERIOD_MS;
+ }
+}
diff --git a/tools/winscope/src/app/components/snack_bar_component.ts b/tools/winscope/src/app/components/snack_bar_component.ts
new file mode 100644
index 0000000..e94d00c
--- /dev/null
+++ b/tools/winscope/src/app/components/snack_bar_component.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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 {Component, Inject} from '@angular/core';
+import {MatSnackBarRef, MAT_SNACK_BAR_DATA} from '@angular/material/snack-bar';
+
+@Component({
+ selector: 'snack-bar',
+ template: `
+ <div class="snack-bar-container">
+ <p *ngFor="let message of messages" class="mat-body-1">
+ {{ message }}
+ </p>
+ <button color="primary" mat-button class="snack-bar-action" (click)="snackBarRef.dismiss()">
+ Close
+ </button>
+ </div>
+ `,
+ styles: [
+ `
+ .snack-bar-container {
+ display: flex;
+ flex-direction: column;
+ }
+ .snack-bar-action {
+ margin-left: 12px;
+ }
+ `,
+ ],
+})
+export class SnackBarComponent {
+ constructor(
+ @Inject(MatSnackBarRef) public snackBarRef: MatSnackBarRef<SnackBarComponent>,
+ @Inject(MAT_SNACK_BAR_DATA) public messages: string[]
+ ) {}
+}
diff --git a/tools/winscope/src/app/components/snack_bar_opener.ts b/tools/winscope/src/app/components/snack_bar_opener.ts
new file mode 100644
index 0000000..d9c35c6
--- /dev/null
+++ b/tools/winscope/src/app/components/snack_bar_opener.ts
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023 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 {Inject, Injectable, NgZone} from '@angular/core';
+import {MatSnackBar} from '@angular/material/snack-bar';
+import {TRACE_INFO} from 'app/trace_info';
+import {UserNotificationListener} from 'interfaces/user_notification_listener';
+import {ParserError, ParserErrorType} from 'parsers/parser_factory';
+import {SnackBarComponent} from './snack_bar_component';
+
+@Injectable({providedIn: 'root'})
+export class SnackBarOpener implements UserNotificationListener {
+ constructor(
+ @Inject(NgZone) private ngZone: NgZone,
+ @Inject(MatSnackBar) private snackBar: MatSnackBar
+ ) {}
+
+ onParserErrors(errors: ParserError[]) {
+ const messages = this.convertErrorsToMessages(errors);
+
+ if (messages.length === 0) {
+ return;
+ }
+
+ this.ngZone.run(() => {
+ // The snackbar needs to be opened within ngZone,
+ // otherwise it will first display on the left and then will jump to the center
+ this.snackBar.openFromComponent(SnackBarComponent, {
+ data: messages,
+ duration: 10000,
+ });
+ });
+ }
+
+ private convertErrorsToMessages(errors: ParserError[]): string[] {
+ const messages: string[] = [];
+ const groups = this.groupErrorsByType(errors);
+
+ for (const [type, groupedErrors] of groups) {
+ const CROP_THRESHOLD = 5;
+ const countUsed = Math.min(groupedErrors.length, CROP_THRESHOLD);
+ const countCropped = groupedErrors.length - countUsed;
+
+ groupedErrors.slice(0, countUsed).forEach((error) => {
+ messages.push(this.convertErrorToMessage(error));
+ });
+
+ if (countCropped > 0) {
+ messages.push(this.makeCroppedMessage(type, countCropped));
+ }
+ }
+
+ return messages;
+ }
+
+ private convertErrorToMessage(error: ParserError): string {
+ const fileName = error.trace !== undefined ? error.trace : '<no file name>';
+ const traceTypeName =
+ error.traceType !== undefined ? TRACE_INFO[error.traceType].name : '<unknown>';
+
+ switch (error.type) {
+ case ParserErrorType.NO_INPUT_FILES:
+ return 'No input files';
+ case ParserErrorType.UNSUPPORTED_FORMAT:
+ return `${fileName}: unsupported file format`;
+ case ParserErrorType.OVERRIDE: {
+ return `${fileName}: overridden by another trace of type ${traceTypeName}`;
+ }
+ default:
+ return `${fileName}: unknown error occurred`;
+ }
+ }
+
+ private makeCroppedMessage(type: ParserErrorType, count: number): string {
+ switch (type) {
+ case ParserErrorType.OVERRIDE:
+ return `... (cropped ${count} overridden trace messages)`;
+ case ParserErrorType.UNSUPPORTED_FORMAT:
+ return `... (cropped ${count} unsupported file format messages)`;
+ default:
+ return `... (cropped ${count} unknown error messages)`;
+ }
+ }
+
+ private groupErrorsByType(errors: ParserError[]): Map<ParserErrorType, ParserError[]> {
+ const groups = new Map<ParserErrorType, ParserError[]>();
+
+ errors.forEach((error) => {
+ if (groups.get(error.type) === undefined) {
+ groups.set(error.type, []);
+ }
+ groups.get(error.type)!.push(error);
+ });
+
+ return groups;
+ }
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/app/components/snack_bar_opener_stub.ts
similarity index 63%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/app/components/snack_bar_opener_stub.ts
index bfe19ce..8852bc3 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/app/components/snack_bar_opener_stub.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {UserNotificationListener} from 'interfaces/user_notification_listener';
+import {ParserError} from 'parsers/parser_factory';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class SnackBarOpenerStub implements UserNotificationListener {
+ onParserErrors(errors: ParserError[]) {
+ // do nothing
+ }
+}
diff --git a/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts b/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts
new file mode 100644
index 0000000..51a1d9b
--- /dev/null
+++ b/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 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 {
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostListener,
+ Input,
+ Output,
+ QueryList,
+ ViewChild,
+ ViewChildren,
+} from '@angular/core';
+import {TimelineData} from 'app/timeline_data';
+import {TRACE_INFO} from 'app/trace_info';
+import {Timestamp} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {TracePosition} from 'trace/trace_position';
+import {SingleTimelineComponent} from './single_timeline_component';
+
+@Component({
+ selector: 'expanded-timeline',
+ template: `
+ <div id="expanded-timeline-wrapper" #expandedTimelineWrapper>
+ <div
+ *ngFor="let trace of getTraces(); trackBy: trackTraceBySelectedTimestamp"
+ class="timeline">
+ <div class="icon-wrapper">
+ <mat-icon
+ class="icon"
+ [matTooltip]="TRACE_INFO[trace.type].name"
+ [style]="{color: TRACE_INFO[trace.type].color}">
+ {{ TRACE_INFO[trace.type].icon }}
+ </mat-icon>
+ </div>
+ <single-timeline
+ [color]="TRACE_INFO[trace.type].color"
+ [trace]="trace"
+ [selectedEntry]="timelineData.findCurrentEntryFor(trace.type)"
+ [selectionRange]="timelineData.getSelectionTimeRange()"
+ (onTracePositionUpdate)="onTracePositionUpdate.emit($event)"
+ class="single-timeline"></single-timeline>
+ <div class="icon-wrapper">
+ <mat-icon class="icon placeholder-icon"></mat-icon>
+ </div>
+ </div>
+
+ <!-- A filler row matching the format and colors of filled rows but with no content -->
+ <div class="timeline units-row">
+ <div class="icon-wrapper">
+ <mat-icon class="icon placeholder-icon"></mat-icon>
+ </div>
+ <div class="single-timeline"></div>
+ <div class="icon-wrapper">
+ <mat-icon class="icon placeholder-icon"></mat-icon>
+ </div>
+ </div>
+
+ <!-- TODO: Implement properly later when we have more time -->
+ <!-- <div id="pointer-overlay" class="timeline">
+ <div class="icon-wrapper" [style]="{ visibility: 'hidden' }">
+ <mat-icon class="icon placeholder-icon">home</mat-icon>
+ </div>
+ <selection-cursor
+ class="selection-cursor"
+ [currentTimestamp]="currentTimestamp"
+ [from]="presenter.selection.from"
+ [to]="presenter.selection.to"
+ ></selection-cursor>
+ </div> -->
+ </div>
+ `,
+ styles: [
+ `
+ #expanded-timeline-wrapper {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ position: relative;
+ }
+ #pointer-overlay {
+ pointer-events: none;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ align-items: stretch;
+ }
+ .timeline {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ }
+ .timeline .single-timeline {
+ flex-grow: 1;
+ }
+ .selection-cursor {
+ flex-grow: 1;
+ }
+ .timeline {
+ border-bottom: 1px solid #f1f3f4;
+ }
+ .icon-wrapper {
+ background-color: #f1f3f4;
+ align-self: stretch;
+ display: flex;
+ justify-content: center;
+ }
+ .icon {
+ margin: 1rem;
+ align-self: center;
+ }
+ .units-row {
+ flex-grow: 1;
+ align-self: baseline;
+ }
+ .units-row .placeholder-icon {
+ visibility: hidden;
+ }
+ `,
+ ],
+})
+export class ExpandedTimelineComponent {
+ @Input() timelineData!: TimelineData;
+ @Output() onTracePositionUpdate = new EventEmitter<TracePosition>();
+
+ @ViewChild('canvas', {static: false}) canvasRef!: ElementRef<HTMLCanvasElement>;
+ @ViewChild('expandedTimelineWrapper', {static: false}) warpperRef!: ElementRef;
+ @ViewChildren(SingleTimelineComponent) singleTimelines!: QueryList<SingleTimelineComponent>;
+
+ TRACE_INFO = TRACE_INFO;
+
+ getTraces(): Array<Trace<{}>> {
+ const traces = new Array<Trace<{}>>();
+ this.timelineData.getTraces().forEachTrace((trace) => {
+ traces.push(trace);
+ });
+ return traces;
+ }
+
+ @HostListener('window:resize', ['$event'])
+ onResize(event: Event) {
+ this.resizeCanvases();
+ }
+
+ trackTraceBySelectedTimestamp = (index: number, trace: Trace<{}>): Timestamp | undefined => {
+ return this.timelineData.findCurrentEntryFor(trace.type)?.getTimestamp();
+ };
+
+ private resizeCanvases() {
+ // Reset any size before computing new size to avoid it interfering with size computations.
+ // Needs to be done together because otherwise the sizes of each timeline will interfere with
+ // each other, since if one timeline is still too big the container will stretch to that size.
+ for (const timeline of this.singleTimelines) {
+ timeline.canvas.width = 0;
+ timeline.canvas.height = 0;
+ timeline.canvas.style.width = 'auto';
+ timeline.canvas.style.height = 'auto';
+ }
+
+ for (const timeline of this.singleTimelines) {
+ timeline.initializeCanvas();
+ timeline.canvas.height = 0;
+ timeline.canvas.style.width = 'auto';
+ timeline.canvas.style.height = 'auto';
+ }
+ }
+}
diff --git a/tools/winscope/src/app/components/timeline/mini_canvas_drawer.ts b/tools/winscope/src/app/components/timeline/mini_canvas_drawer.ts
new file mode 100644
index 0000000..14c6b77
--- /dev/null
+++ b/tools/winscope/src/app/components/timeline/mini_canvas_drawer.ts
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2022 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 {TimeRange} from 'app/timeline_data';
+import {TRACE_INFO} from 'app/trace_info';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {Color} from '../../colors';
+import {CanvasDrawer} from '../canvas/canvas_drawer';
+import {CanvasMouseHandler} from '../canvas/canvas_mouse_handler';
+import {DraggableCanvasObject} from '../canvas/draggable_canvas_object';
+import {Segment} from './utils';
+
+export class MiniCanvasDrawerInput {
+ constructor(
+ public fullRange: TimeRange,
+ public selectedPosition: Timestamp,
+ public selection: TimeRange,
+ public traces: Traces
+ ) {}
+
+ transform(mapToRange: Segment): MiniCanvasDrawerData {
+ const transformer = new Transformer(this.fullRange, mapToRange);
+ return new MiniCanvasDrawerData(
+ transformer.transform(this.selectedPosition),
+ {
+ from: transformer.transform(this.selection.from),
+ to: transformer.transform(this.selection.to),
+ },
+ this.transformTracesTimestamps(transformer),
+ transformer
+ );
+ }
+
+ private transformTracesTimestamps(transformer: Transformer): Map<TraceType, number[]> {
+ const transformedTraceSegments = new Map<TraceType, number[]>();
+
+ this.traces.forEachTrace((trace) => {
+ transformedTraceSegments.set(trace.type, this.transformTraceTimestamps(transformer, trace));
+ });
+
+ return transformedTraceSegments;
+ }
+
+ private transformTraceTimestamps(transformer: Transformer, trace: Trace<{}>): number[] {
+ const result: number[] = [];
+
+ trace.forEachTimestamp((timestamp) => {
+ result.push(transformer.transform(timestamp));
+ });
+
+ return result;
+ }
+}
+
+export class Transformer {
+ private timestampType: TimestampType;
+
+ private fromWidth: bigint;
+ private targetWidth: number;
+
+ private fromOffset: bigint;
+ private toOffset: number;
+
+ constructor(private fromRange: TimeRange, private toRange: Segment) {
+ this.timestampType = fromRange.from.getType();
+
+ this.fromWidth = this.fromRange.to.getValueNs() - this.fromRange.from.getValueNs();
+ // Needs to be a whole number to be compatible with bigints
+ this.targetWidth = Math.round(this.toRange.to - this.toRange.from);
+
+ this.fromOffset = this.fromRange.from.getValueNs();
+ // Needs to be a whole number to be compatible with bigints
+ this.toOffset = Math.round(this.toRange.from);
+ }
+
+ transform(x: Timestamp): number {
+ return (
+ this.toOffset +
+ (this.targetWidth * Number(x.getValueNs() - this.fromOffset)) / Number(this.fromWidth)
+ );
+ }
+
+ untransform(x: number): Timestamp {
+ x = Math.round(x);
+ const valueNs =
+ this.fromOffset + (BigInt(x - this.toOffset) * this.fromWidth) / BigInt(this.targetWidth);
+ return new Timestamp(this.timestampType, valueNs);
+ }
+}
+
+class MiniCanvasDrawerOutput {
+ constructor(public selectedPosition: Timestamp, public selection: TimeRange) {}
+}
+
+class MiniCanvasDrawerData {
+ constructor(
+ public selectedPosition: number,
+ public selection: Segment,
+ public timelineEntries: Map<TraceType, number[]>,
+ public transformer: Transformer
+ ) {}
+
+ toOutput(): MiniCanvasDrawerOutput {
+ return new MiniCanvasDrawerOutput(this.transformer.untransform(this.selectedPosition), {
+ from: this.transformer.untransform(this.selection.from),
+ to: this.transformer.untransform(this.selection.to),
+ });
+ }
+}
+
+export class MiniCanvasDrawer implements CanvasDrawer {
+ ctx: CanvasRenderingContext2D;
+ handler: CanvasMouseHandler;
+
+ private activePointer: DraggableCanvasObject;
+ private leftFocusSectionSelector: DraggableCanvasObject;
+ private rightFocusSectionSelector: DraggableCanvasObject;
+
+ private get pointerWidth() {
+ return this.getHeight() / 6;
+ }
+
+ getXScale() {
+ return this.ctx.getTransform().m11;
+ }
+
+ getYScale() {
+ return this.ctx.getTransform().m22;
+ }
+
+ getWidth() {
+ return this.canvas.width / this.getXScale();
+ }
+
+ getHeight() {
+ return this.canvas.height / this.getYScale();
+ }
+
+ get usableRange() {
+ return {
+ from: this.padding.left,
+ to: this.getWidth() - this.padding.left - this.padding.right,
+ };
+ }
+
+ get input() {
+ return this.inputGetter().transform(this.usableRange);
+ }
+
+ constructor(
+ public canvas: HTMLCanvasElement,
+ private inputGetter: () => MiniCanvasDrawerInput,
+ private onPointerPositionDragging: (pos: Timestamp) => void,
+ private onPointerPositionChanged: (pos: Timestamp) => void,
+ private onSelectionChanged: (selection: TimeRange) => void,
+ private onUnhandledClick: (pos: Timestamp) => void
+ ) {
+ const ctx = canvas.getContext('2d');
+
+ if (ctx === null) {
+ throw Error('MiniTimeline canvas context was null!');
+ }
+
+ this.ctx = ctx;
+
+ const onUnhandledClickInternal = (x: number, y: number) => {
+ this.onUnhandledClick(this.input.transformer.untransform(x));
+ };
+ this.handler = new CanvasMouseHandler(this, 'pointer', onUnhandledClickInternal);
+
+ this.activePointer = new DraggableCanvasObject(
+ this,
+ () => this.selectedPosition,
+ (ctx: CanvasRenderingContext2D, position: number) => {
+ const barWidth = 3;
+ const triangleHeight = this.pointerWidth / 2;
+
+ ctx.beginPath();
+ ctx.moveTo(position - triangleHeight, 0);
+ ctx.lineTo(position + triangleHeight, 0);
+ ctx.lineTo(position + barWidth / 2, triangleHeight);
+ ctx.lineTo(position + barWidth / 2, this.getHeight());
+ ctx.lineTo(position - barWidth / 2, this.getHeight());
+ ctx.lineTo(position - barWidth / 2, triangleHeight);
+ ctx.closePath();
+ },
+ {
+ fillStyle: Color.ACTIVE_POINTER,
+ fill: true,
+ },
+ (x) => {
+ this.input.selectedPosition = x;
+ this.onPointerPositionDragging(this.input.transformer.untransform(x));
+ },
+ (x) => {
+ this.input.selectedPosition = x;
+ this.onPointerPositionChanged(this.input.transformer.untransform(x));
+ },
+ () => this.usableRange
+ );
+
+ const focusSelectorDrawConfig = {
+ fillStyle: Color.SELECTOR_COLOR,
+ fill: true,
+ };
+
+ const onLeftSelectionChanged = (x: number) => {
+ this.selection.from = x;
+ this.onSelectionChanged({
+ from: this.input.transformer.untransform(x),
+ to: this.input.transformer.untransform(this.selection.to),
+ });
+ };
+ const onRightSelectionChanged = (x: number) => {
+ this.selection.to = x;
+ this.onSelectionChanged({
+ from: this.input.transformer.untransform(this.selection.from),
+ to: this.input.transformer.untransform(x),
+ });
+ };
+
+ const barWidth = 6;
+ const selectorArrowWidth = this.innerHeight / 12;
+ const selectorArrowHeight = selectorArrowWidth * 2;
+
+ this.leftFocusSectionSelector = new DraggableCanvasObject(
+ this,
+ () => this.selection.from,
+ (ctx: CanvasRenderingContext2D, position: number) => {
+ ctx.beginPath();
+ ctx.moveTo(position - barWidth, this.padding.top);
+ ctx.lineTo(position, this.padding.top);
+ ctx.lineTo(position + selectorArrowWidth, this.padding.top + selectorArrowWidth);
+ ctx.lineTo(position, this.padding.top + selectorArrowHeight);
+ ctx.lineTo(position, this.padding.top + this.innerHeight);
+ ctx.lineTo(position - barWidth, this.padding.top + this.innerHeight);
+ ctx.lineTo(position - barWidth, this.padding.top);
+ ctx.closePath();
+ },
+ focusSelectorDrawConfig,
+ onLeftSelectionChanged,
+ onLeftSelectionChanged,
+ () => {
+ return {
+ from: this.usableRange.from,
+ to: this.rightFocusSectionSelector.position - selectorArrowWidth - barWidth,
+ };
+ }
+ );
+
+ this.rightFocusSectionSelector = new DraggableCanvasObject(
+ this,
+ () => this.selection.to,
+ (ctx: CanvasRenderingContext2D, position: number) => {
+ ctx.beginPath();
+ ctx.moveTo(position + barWidth, this.padding.top);
+ ctx.lineTo(position, this.padding.top);
+ ctx.lineTo(position - selectorArrowWidth, this.padding.top + selectorArrowWidth);
+ ctx.lineTo(position, this.padding.top + selectorArrowHeight);
+ ctx.lineTo(position, this.padding.top + this.innerHeight);
+ ctx.lineTo(position + barWidth, this.padding.top + this.innerHeight);
+ ctx.closePath();
+ },
+ focusSelectorDrawConfig,
+ onRightSelectionChanged,
+ onRightSelectionChanged,
+ () => {
+ return {
+ from: this.leftFocusSectionSelector.position + selectorArrowWidth + barWidth,
+ to: this.usableRange.to,
+ };
+ }
+ );
+ }
+
+ get selectedPosition() {
+ return this.input.selectedPosition;
+ }
+
+ get selection() {
+ return this.input.selection;
+ }
+
+ get timelineEntries() {
+ return this.input.timelineEntries;
+ }
+
+ get padding() {
+ return {
+ top: Math.ceil(this.getHeight() / 5),
+ bottom: Math.ceil(this.getHeight() / 5),
+ left: Math.ceil(this.pointerWidth / 2),
+ right: Math.ceil(this.pointerWidth / 2),
+ };
+ }
+
+ get innerHeight() {
+ return this.getHeight() - this.padding.top - this.padding.bottom;
+ }
+
+ draw() {
+ this.ctx.clearRect(0, 0, this.getWidth(), this.getHeight());
+
+ this.drawSelectionBackground();
+
+ this.drawTraceLines();
+
+ this.drawTimelineGuides();
+
+ this.leftFocusSectionSelector.draw(this.ctx);
+ this.rightFocusSectionSelector.draw(this.ctx);
+
+ this.activePointer.draw(this.ctx);
+ }
+
+ private drawSelectionBackground() {
+ const triangleHeight = this.innerHeight / 6;
+
+ // Selection background
+ this.ctx.globalAlpha = 0.8;
+ this.ctx.fillStyle = Color.SELECTION_BACKGROUND;
+ const width = this.selection.to - this.selection.from;
+ this.ctx.fillRect(
+ this.selection.from,
+ this.padding.top + triangleHeight / 2,
+ width,
+ this.innerHeight - triangleHeight / 2
+ );
+ this.ctx.restore();
+ }
+
+ private drawTraceLines() {
+ const lineHeight = this.innerHeight / 8;
+
+ let fromTop = this.padding.top + (this.innerHeight * 2) / 3 - lineHeight;
+
+ this.timelineEntries.forEach((entries, traceType) => {
+ // TODO: Only if active or a selected trace
+ for (const entry of entries) {
+ this.ctx.globalAlpha = 0.7;
+ this.ctx.fillStyle = TRACE_INFO[traceType].color;
+
+ const width = 5;
+ this.ctx.fillRect(entry - width / 2, fromTop, width, lineHeight);
+ this.ctx.globalAlpha = 1.0;
+ }
+
+ fromTop -= (lineHeight * 4) / 3;
+ });
+ }
+
+ private drawTimelineGuides() {
+ const edgeBarHeight = (this.innerHeight * 1) / 2;
+ const edgeBarWidth = 4;
+
+ const boldBarHeight = (this.innerHeight * 1) / 5;
+ const boldBarWidth = edgeBarWidth;
+
+ const lightBarHeight = (this.innerHeight * 1) / 6;
+ const lightBarWidth = 2;
+
+ const minSpacing = lightBarWidth * 7;
+ const barsInSetWidth = 9 * lightBarWidth + boldBarWidth;
+ const barSets = Math.floor(
+ (this.getWidth() - edgeBarWidth * 2 - minSpacing) / (barsInSetWidth + 10 * minSpacing)
+ );
+ const bars = barSets * 10;
+
+ // Draw start bar
+ this.ctx.fillStyle = Color.GUIDE_BAR;
+ this.ctx.fillRect(
+ 0,
+ this.padding.top + this.innerHeight - edgeBarHeight,
+ edgeBarWidth,
+ edgeBarHeight
+ );
+
+ // Draw end bar
+ this.ctx.fillStyle = Color.GUIDE_BAR;
+ this.ctx.fillRect(
+ this.getWidth() - edgeBarWidth,
+ this.padding.top + this.innerHeight - edgeBarHeight,
+ edgeBarWidth,
+ edgeBarHeight
+ );
+
+ const spacing = (this.getWidth() - barSets * barsInSetWidth - edgeBarWidth) / bars;
+ let start = edgeBarWidth + spacing;
+ for (let i = 1; i < bars; i++) {
+ if (i % 10 === 0) {
+ // Draw boldbar
+ this.ctx.fillStyle = Color.GUIDE_BAR;
+ this.ctx.fillRect(
+ start,
+ this.padding.top + this.innerHeight - boldBarHeight,
+ boldBarWidth,
+ boldBarHeight
+ );
+ start += boldBarWidth; // TODO: Shift a bit
+ } else {
+ // Draw lightbar
+ this.ctx.fillStyle = Color.GUIDE_BAR_LIGHT;
+ this.ctx.fillRect(
+ start,
+ this.padding.top + this.innerHeight - lightBarHeight,
+ lightBarWidth,
+ lightBarHeight
+ );
+ start += lightBarWidth;
+ }
+ start += spacing;
+ }
+ }
+}
diff --git a/tools/winscope/src/app/components/timeline/mini_timeline_component.ts b/tools/winscope/src/app/components/timeline/mini_timeline_component.ts
new file mode 100644
index 0000000..89230a3
--- /dev/null
+++ b/tools/winscope/src/app/components/timeline/mini_timeline_component.ts
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 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 {
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostListener,
+ Input,
+ Output,
+ SimpleChanges,
+ ViewChild,
+} from '@angular/core';
+import {TimelineData} from 'app/timeline_data';
+import {assertDefined} from 'common/assert_utils';
+import {Timestamp} from 'trace/timestamp';
+import {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {MiniCanvasDrawer, MiniCanvasDrawerInput} from './mini_canvas_drawer';
+
+@Component({
+ selector: 'mini-timeline',
+ template: `
+ <div id="mini-timeline-wrapper" #miniTimelineWrapper>
+ <canvas #canvas></canvas>
+ </div>
+ `,
+ styles: [
+ `
+ #mini-timeline-wrapper {
+ width: 100%;
+ min-height: 5em;
+ height: 100%;
+ }
+ `,
+ ],
+})
+export class MiniTimelineComponent {
+ @Input() timelineData!: TimelineData;
+ @Input() currentTracePosition!: TracePosition;
+ @Input() selectedTraces!: TraceType[];
+
+ @Output() onTracePositionUpdate = new EventEmitter<TracePosition>();
+ @Output() onSeekTimestampUpdate = new EventEmitter<Timestamp | undefined>();
+
+ @ViewChild('miniTimelineWrapper', {static: false}) miniTimelineWrapper!: ElementRef;
+ @ViewChild('canvas', {static: false}) canvasRef!: ElementRef;
+ get canvas(): HTMLCanvasElement {
+ return this.canvasRef.nativeElement;
+ }
+
+ private drawer: MiniCanvasDrawer | undefined = undefined;
+
+ ngAfterViewInit(): void {
+ this.makeHiPPICanvas();
+
+ const updateTimestampCallback = (timestamp: Timestamp) => {
+ this.onSeekTimestampUpdate.emit(undefined);
+ this.onTracePositionUpdate.emit(TracePosition.fromTimestamp(timestamp));
+ };
+
+ this.drawer = new MiniCanvasDrawer(
+ this.canvas,
+ () => this.getMiniCanvasDrawerInput(),
+ (position) => {
+ const timestampType = this.timelineData.getTimestampType()!;
+ this.onSeekTimestampUpdate.emit(position);
+ },
+ updateTimestampCallback,
+ (selection) => {
+ const timestampType = this.timelineData.getTimestampType()!;
+ this.timelineData.setSelectionTimeRange(selection);
+ },
+ updateTimestampCallback
+ );
+ this.drawer.draw();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.drawer !== undefined) {
+ this.drawer.draw();
+ }
+ }
+
+ private getMiniCanvasDrawerInput() {
+ return new MiniCanvasDrawerInput(
+ this.timelineData.getFullTimeRange(),
+ this.currentTracePosition.timestamp,
+ this.timelineData.getSelectionTimeRange(),
+ this.getTracesToShow()
+ );
+ }
+
+ private getTracesToShow(): Traces {
+ const traces = new Traces();
+ this.selectedTraces.forEach((type) => {
+ traces.setTrace(type, assertDefined(this.timelineData.getTraces().getTrace(type)));
+ });
+ return traces;
+ }
+
+ private makeHiPPICanvas() {
+ // Reset any size before computing new size to avoid it interfering with size computations
+ this.canvas.width = 0;
+ this.canvas.height = 0;
+ this.canvas.style.width = 'auto';
+ this.canvas.style.height = 'auto';
+
+ const width = this.miniTimelineWrapper.nativeElement.offsetWidth;
+ const height = this.miniTimelineWrapper.nativeElement.offsetHeight;
+
+ const HiPPIwidth = window.devicePixelRatio * width;
+ const HiPPIheight = window.devicePixelRatio * height;
+
+ this.canvas.width = HiPPIwidth;
+ this.canvas.height = HiPPIheight;
+ this.canvas.style.width = width + 'px';
+ this.canvas.style.height = height + 'px';
+
+ // ensure all drawing operations are scaled
+ if (window.devicePixelRatio !== 1) {
+ const context = this.canvas.getContext('2d')!;
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
+ }
+ }
+
+ @HostListener('window:resize', ['$event'])
+ onResize(event: Event) {
+ this.makeHiPPICanvas();
+ this.drawer?.draw();
+ }
+}
diff --git a/tools/winscope/src/app/components/timeline/single_timeline_component.ts b/tools/winscope/src/app/components/timeline/single_timeline_component.ts
new file mode 100644
index 0000000..290d246
--- /dev/null
+++ b/tools/winscope/src/app/components/timeline/single_timeline_component.ts
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2022 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 {
+ Component,
+ ElementRef,
+ EventEmitter,
+ Input,
+ Output,
+ SimpleChanges,
+ ViewChild,
+} from '@angular/core';
+import {Color} from 'app/colors';
+import {TimeRange} from 'app/timeline_data';
+import {Timestamp} from 'trace/timestamp';
+import {Trace, TraceEntry} from 'trace/trace';
+import {TracePosition} from 'trace/trace_position';
+
+@Component({
+ selector: 'single-timeline',
+ template: `
+ <div class="single-timeline" #wrapper>
+ <canvas #canvas></canvas>
+ </div>
+ `,
+ styles: [
+ `
+ .single-timeline {
+ height: 2rem;
+ padding: 1rem 0;
+ }
+ `,
+ ],
+})
+export class SingleTimelineComponent {
+ @Input() color = '#AF5CF7';
+ @Input() trace!: Trace<{}>;
+ @Input() selectedEntry: TraceEntry<{}> | undefined = undefined;
+ @Input() selectionRange!: TimeRange;
+
+ @Output() onTracePositionUpdate = new EventEmitter<TracePosition>();
+
+ @ViewChild('canvas', {static: false}) canvasRef!: ElementRef;
+ @ViewChild('wrapper', {static: false}) wrapperRef!: ElementRef;
+
+ hoveringEntry?: Timestamp;
+
+ private viewInitialized = false;
+
+ get canvas() {
+ return this.canvasRef.nativeElement;
+ }
+
+ get ctx(): CanvasRenderingContext2D {
+ const ctx = this.canvas.getContext('2d');
+
+ if (ctx == null) {
+ throw Error('Failed to get canvas context!');
+ }
+
+ return ctx;
+ }
+
+ ngOnInit() {
+ if (!this.trace || !this.selectionRange) {
+ throw Error('Not all required inputs have been set');
+ }
+ }
+
+ ngAfterViewInit() {
+ // TODO: Clear observer at some point
+ new ResizeObserver(() => this.initializeCanvas()).observe(this.wrapperRef.nativeElement);
+ this.initializeCanvas();
+ }
+
+ initializeCanvas() {
+ // Reset any size before computing new size to avoid it interfering with size computations
+ this.canvas.width = 0;
+ this.canvas.height = 0;
+ this.canvas.style.width = 'auto';
+ this.canvas.style.height = 'auto';
+
+ const computedStyle = getComputedStyle(this.wrapperRef.nativeElement);
+ const width = this.wrapperRef.nativeElement.offsetWidth;
+ const height =
+ this.wrapperRef.nativeElement.offsetHeight -
+ // tslint:disable-next-line:ban
+ parseFloat(computedStyle.paddingTop) -
+ // tslint:disable-next-line:ban
+ parseFloat(computedStyle.paddingBottom);
+
+ const HiPPIwidth = window.devicePixelRatio * width;
+ const HiPPIheight = window.devicePixelRatio * height;
+
+ this.canvas.width = HiPPIwidth;
+ this.canvas.height = HiPPIheight;
+ this.canvas.style.width = width + 'px';
+ this.canvas.style.height = height + 'px';
+
+ // ensure all drawing operations are scaled
+ if (window.devicePixelRatio !== 1) {
+ const context = this.canvas.getContext('2d')!;
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
+ }
+
+ this.redraw();
+
+ this.canvas.addEventListener('mousemove', (event: MouseEvent) => {
+ this.handleMouseMove(event);
+ });
+ this.canvas.addEventListener('mousedown', (event: MouseEvent) => {
+ this.handleMouseDown(event);
+ });
+ this.canvas.addEventListener('mouseout', (event: MouseEvent) => {
+ this.handleMouseOut(event);
+ });
+
+ this.viewInitialized = true;
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.viewInitialized) {
+ this.redraw();
+ }
+ }
+
+ private handleMouseOut(e: MouseEvent) {
+ if (this.hoveringEntry) {
+ // If undefined there is no current hover effect so no need to clear
+ this.redraw();
+ }
+ this.hoveringEntry = undefined;
+ }
+
+ getXScale(): number {
+ return this.ctx.getTransform().m11;
+ }
+
+ getYScale(): number {
+ return this.ctx.getTransform().m22;
+ }
+
+ private handleMouseMove(e: MouseEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ const mouseX = e.offsetX * this.getXScale();
+ const mouseY = e.offsetY * this.getYScale();
+
+ this.updateCursor(mouseX, mouseY);
+ this.drawEntryHover(mouseX, mouseY);
+ }
+
+ private drawEntryHover(mouseX: number, mouseY: number) {
+ const currentHoverEntry = this.getEntryAt(mouseX, mouseY);
+ if (this.hoveringEntry === currentHoverEntry) {
+ return;
+ }
+
+ if (this.hoveringEntry) {
+ // If null there is no current hover effect so no need to clear
+ this.clearCanvas();
+ this.drawTimeline();
+ }
+
+ this.hoveringEntry = currentHoverEntry;
+
+ if (!this.hoveringEntry) {
+ return;
+ }
+
+ this.defineEntryPath(this.hoveringEntry);
+
+ this.ctx.globalAlpha = 1.0;
+ this.ctx.strokeStyle = Color.ACTIVE_BORDER;
+ this.ctx.lineWidth = 2;
+ this.ctx.save();
+ this.ctx.clip();
+ this.ctx.lineWidth *= 2;
+ this.ctx.fill();
+ this.ctx.stroke();
+ this.ctx.restore();
+ this.ctx.stroke();
+
+ this.ctx.restore();
+ }
+
+ private clearCanvas() {
+ // Clear canvas
+ this.ctx.clearRect(0, 0, this.getScaledCanvasWidth(), this.getScaledCanvasHeight());
+ }
+
+ private getEntryAt(mouseX: number, mouseY: number): Timestamp | undefined {
+ // TODO: This can be optimized if it's laggy
+ for (let i = 0; i < this.trace.lengthEntries; ++i) {
+ const timestamp = this.trace.getEntry(i).getTimestamp();
+ this.defineEntryPath(timestamp);
+ if (this.ctx.isPointInPath(mouseX, mouseY)) {
+ this.canvas.style.cursor = 'pointer';
+ return timestamp;
+ }
+ }
+ return undefined;
+ }
+
+ private updateCursor(mouseX: number, mouseY: number) {
+ if (this.getEntryAt(mouseX, mouseY)) {
+ this.canvas.style.cursor = 'pointer';
+ }
+ this.canvas.style.cursor = 'auto';
+ }
+
+ private handleMouseDown(e: MouseEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ const mouseX = e.offsetX * this.getXScale();
+ const mouseY = e.offsetY * this.getYScale();
+
+ const clickedTimestamp = this.getEntryAt(mouseX, mouseY);
+
+ if (
+ clickedTimestamp &&
+ clickedTimestamp.getValueNs() !== this.selectedEntry?.getTimestamp().getValueNs()
+ ) {
+ this.redraw();
+ const entry = this.trace.findClosestEntry(clickedTimestamp);
+ if (entry) {
+ this.selectedEntry = entry;
+ this.onTracePositionUpdate.emit(TracePosition.fromTraceEntry(entry));
+ }
+ }
+ }
+
+ getScaledCanvasWidth() {
+ return Math.floor(this.canvas.width / this.getXScale());
+ }
+
+ getScaledCanvasHeight() {
+ return Math.floor(this.canvas.height / this.getYScale());
+ }
+
+ get entryWidth() {
+ return this.getScaledCanvasHeight();
+ }
+
+ get availableWidth() {
+ return Math.floor(this.getScaledCanvasWidth() - this.entryWidth);
+ }
+
+ private defineEntryPath(entry: Timestamp, padding = 0) {
+ const start = this.selectionRange.from.getValueNs();
+ const end = this.selectionRange.to.getValueNs();
+
+ const xPos = Number(
+ (BigInt(this.availableWidth) * (entry.getValueNs() - start)) / (end - start)
+ );
+
+ rect(
+ this.ctx,
+ xPos + padding,
+ padding,
+ this.entryWidth - 2 * padding,
+ this.entryWidth - 2 * padding
+ );
+ }
+
+ private redraw() {
+ this.clearCanvas();
+ this.drawTimeline();
+ }
+
+ private drawTimeline() {
+ this.trace.forEachTimestamp((entry) => {
+ this.drawEntry(entry);
+ });
+ this.drawSelectedEntry();
+ }
+
+ private drawEntry(entry: Timestamp) {
+ this.ctx.globalAlpha = 0.2;
+
+ this.defineEntryPath(entry);
+ this.ctx.fillStyle = this.color;
+ this.ctx.fill();
+
+ this.ctx.restore();
+ }
+
+ private drawSelectedEntry() {
+ if (this.selectedEntry === undefined) {
+ return;
+ }
+
+ this.ctx.globalAlpha = 1.0;
+ this.defineEntryPath(this.selectedEntry.getTimestamp(), 1);
+ this.ctx.fillStyle = this.color;
+ this.ctx.strokeStyle = Color.ACTIVE_BORDER;
+ this.ctx.lineWidth = 3;
+ this.ctx.stroke();
+ this.ctx.fill();
+
+ this.ctx.restore();
+ }
+}
+
+function rect(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number) {
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + w, y);
+ ctx.lineTo(x + w, y + h);
+ ctx.lineTo(x, y + h);
+ ctx.lineTo(x, y);
+ ctx.closePath();
+}
diff --git a/tools/winscope/src/app/components/timeline/timeline_component.ts b/tools/winscope/src/app/components/timeline/timeline_component.ts
new file mode 100644
index 0000000..7d2f430
--- /dev/null
+++ b/tools/winscope/src/app/components/timeline/timeline_component.ts
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2022 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 {
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostListener,
+ Inject,
+ Input,
+ Output,
+ ViewChild,
+ ViewEncapsulation,
+} from '@angular/core';
+import {FormControl, FormGroup, Validators} from '@angular/forms';
+import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
+import {TimelineData} from 'app/timeline_data';
+import {TRACE_INFO} from 'app/trace_info';
+import {StringUtils} from 'common/string_utils';
+import {TimeUtils} from 'common/time_utils';
+import {TracePositionUpdateListener} from 'interfaces/trace_position_update_listener';
+import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {MiniTimelineComponent} from './mini_timeline_component';
+
+@Component({
+ selector: 'timeline',
+ encapsulation: ViewEncapsulation.None,
+ template: `
+ <div id="expanded-nav" *ngIf="expanded">
+ <div id="video-content" *ngIf="videoUrl !== undefined">
+ <video
+ *ngIf="getVideoCurrentTime() !== undefined"
+ id="video"
+ [currentTime]="getVideoCurrentTime()"
+ [src]="videoUrl"></video>
+ <div *ngIf="getVideoCurrentTime() === undefined" class="no-video-message">
+ <p>No screenrecording frame to show</p>
+ <p>Current timestamp before first screenrecording frame.</p>
+ </div>
+ </div>
+ <expanded-timeline
+ [timelineData]="timelineData"
+ (onTracePositionUpdate)="updatePosition($event)"
+ id="expanded-timeline"></expanded-timeline>
+ </div>
+ <div class="navbar" #collapsedTimeline>
+ <ng-template [ngIf]="timelineData.hasMoreThanOneDistinctTimestamp()">
+ <div id="time-selector">
+ <button
+ mat-icon-button
+ id="prev_entry_button"
+ color="primary"
+ (click)="moveToPreviousEntry()"
+ [disabled]="!hasPrevEntry()">
+ <mat-icon>chevron_left</mat-icon>
+ </button>
+ <form [formGroup]="timestampForm" class="time-selector-form">
+ <mat-form-field
+ class="time-input"
+ appearance="fill"
+ (change)="humanElapsedTimeInputChange($event)"
+ *ngIf="!usingRealtime()">
+ <input
+ matInput
+ name="humanElapsedTimeInput"
+ [formControl]="selectedElapsedTimeFormControl" />
+ </mat-form-field>
+ <mat-form-field
+ class="time-input"
+ appearance="fill"
+ (change)="humanRealTimeInputChanged($event)"
+ *ngIf="usingRealtime()">
+ <input
+ matInput
+ name="humanRealTimeInput"
+ [formControl]="selectedRealTimeFormControl" />
+ </mat-form-field>
+ <mat-form-field
+ class="time-input"
+ appearance="fill"
+ (change)="nanosecondsInputTimeChange($event)">
+ <input matInput name="nsTimeInput" [formControl]="selectedNsFormControl" />
+ </mat-form-field>
+ </form>
+ <button
+ mat-icon-button
+ id="next_entry_button"
+ color="primary"
+ (click)="moveToNextEntry()"
+ [disabled]="!hasNextEntry()">
+ <mat-icon>chevron_right</mat-icon>
+ </button>
+ </div>
+ <div id="trace-selector">
+ <mat-form-field appearance="none">
+ <mat-select
+ #traceSelector
+ [formControl]="selectedTracesFormControl"
+ multiple
+ (closed)="onTraceSelectionClosed()">
+ <div class="tip">Select up to 2 additional traces to display.</div>
+ <mat-option
+ *ngFor="let trace of availableTraces"
+ [value]="trace"
+ [style]="{
+ color: TRACE_INFO[trace].color,
+ opacity: isOptionDisabled(trace) ? 0.5 : 1.0
+ }"
+ [disabled]="isOptionDisabled(trace)">
+ <mat-icon>{{ TRACE_INFO[trace].icon }}</mat-icon>
+ {{ TRACE_INFO[trace].name }}
+ </mat-option>
+ <div class="actions">
+ <button mat-button color="primary" (click)="traceSelector.close()">Cancel</button>
+ <button
+ mat-flat-button
+ color="primary"
+ (click)="applyNewTraceSelection(); traceSelector.close()">
+ Apply
+ </button>
+ </div>
+ <mat-select-trigger class="shown-selection">
+ <mat-icon
+ *ngFor="let selectedTrace of selectedTraces"
+ [style]="{color: TRACE_INFO[selectedTrace].color}">
+ {{ TRACE_INFO[selectedTrace].icon }}
+ </mat-icon>
+ </mat-select-trigger>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <mini-timeline
+ [timelineData]="timelineData"
+ [currentTracePosition]="getCurrentTracePosition()"
+ [selectedTraces]="selectedTraces"
+ (onTracePositionUpdate)="updatePosition($event)"
+ (onSeekTimestampUpdate)="updateSeekTimestamp($event)"
+ id="mini-timeline"
+ #miniTimeline></mini-timeline>
+ <div id="toggle" *ngIf="timelineData.hasMoreThanOneDistinctTimestamp()">
+ <button
+ mat-icon-button
+ [class]="TOGGLE_BUTTON_CLASS"
+ color="primary"
+ aria-label="Toggle Expanded Timeline"
+ (click)="toggleExpand()">
+ <mat-icon *ngIf="!expanded">expand_less</mat-icon>
+ <mat-icon *ngIf="expanded">expand_more</mat-icon>
+ </button>
+ </div>
+ </ng-template>
+ <div *ngIf="!timelineData.hasTimestamps()" class="no-timestamps-msg">
+ <p class="mat-body-2">No timeline to show!</p>
+ <p class="mat-body-1">All loaded traces contain no timestamps!</p>
+ </div>
+ <div
+ *ngIf="timelineData.hasTimestamps() && !timelineData.hasMoreThanOneDistinctTimestamp()"
+ class="no-timestamps-msg">
+ <p class="mat-body-2">No timeline to show!</p>
+ <p class="mat-body-1">Only a single timestamp has been recorded.</p>
+ </div>
+ </div>
+ `,
+ styles: [
+ `
+ .navbar {
+ display: flex;
+ width: 100%;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ }
+ #expanded-nav {
+ display: flex;
+ border-bottom: 1px solid #3333;
+ }
+ #time-selector {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ }
+ .time-selector-form {
+ display: flex;
+ flex-direction: column;
+ width: 15em;
+ }
+ .time-selector-form .time-input {
+ width: 100%;
+ margin-bottom: -1.34375em;
+ text-align: center;
+ }
+ #mini-timeline {
+ flex-grow: 1;
+ align-self: stretch;
+ }
+ #video-content {
+ position: relative;
+ min-width: 20rem;
+ min-height: 35rem;
+ align-self: stretch;
+ text-align: center;
+ border: 2px solid black;
+ flex-basis: 0px;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ }
+ #video {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 100%;
+ }
+ #expanded-nav {
+ display: flex;
+ flex-direction: row;
+ }
+ #expanded-timeline {
+ flex-grow: 1;
+ }
+ #trace-selector .mat-form-field-infix {
+ width: 50px;
+ padding: 0 0.75rem 0 0.5rem;
+ border-top: unset;
+ }
+ #trace-selector .mat-icon {
+ padding: 2px;
+ }
+ #trace-selector .shown-selection {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: auto;
+ }
+ #trace-selector .mat-select-trigger {
+ height: unset;
+ }
+ #trace-selector .mat-form-field-wrapper {
+ padding: 0;
+ }
+ .mat-select-panel {
+ max-height: unset !important;
+ font-family: 'Roboto', sans-serif;
+ }
+ .tip {
+ padding: 1.5rem;
+ font-weight: 200;
+ border-bottom: solid 1px #dadce0;
+ }
+ .actions {
+ border-top: solid 1px #dadce0;
+ width: 100%;
+ padding: 1.5rem;
+ float: right;
+ display: flex;
+ justify-content: flex-end;
+ }
+ .no-video-message {
+ padding: 1rem;
+ font-family: 'Roboto', sans-serif;
+ }
+ .no-timestamps-msg {
+ padding: 1rem;
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ }
+ `,
+ ],
+})
+export class TimelineComponent implements TracePositionUpdateListener {
+ readonly TOGGLE_BUTTON_CLASS: string = 'button-toggle-expansion';
+ readonly MAX_SELECTED_TRACES = 3;
+
+ @Input() set activeViewTraceTypes(types: TraceType[] | undefined) {
+ if (!types) {
+ return;
+ }
+
+ if (types.length !== 1) {
+ throw Error("Timeline component doesn't support viewers with dependencies length !== 1");
+ }
+
+ this.internalActiveTrace = types[0];
+
+ if (!this.selectedTraces.includes(this.internalActiveTrace)) {
+ this.selectedTraces.push(this.internalActiveTrace);
+ }
+
+ if (this.selectedTraces.length > this.MAX_SELECTED_TRACES) {
+ // Maxed capacity so remove oldest selected trace
+ this.selectedTraces = this.selectedTraces.slice(1, 1 + this.MAX_SELECTED_TRACES);
+ }
+
+ // Create new object to make sure we trigger an update on Mini Timeline child component
+ this.selectedTraces = [...this.selectedTraces];
+ this.selectedTracesFormControl.setValue(this.selectedTraces);
+ }
+ internalActiveTrace: TraceType | undefined = undefined;
+
+ @Input() timelineData!: TimelineData;
+ @Input() availableTraces: TraceType[] = [];
+
+ @Output() collapsedTimelineSizeChanged = new EventEmitter<number>();
+
+ @ViewChild('miniTimeline') private miniTimelineComponent!: MiniTimelineComponent;
+ @ViewChild('collapsedTimeline') private collapsedTimelineRef!: ElementRef;
+
+ selectedTraces: TraceType[] = [];
+ selectedTracesFormControl = new FormControl();
+
+ selectedElapsedTimeFormControl = new FormControl(
+ 'undefined',
+ Validators.compose([
+ Validators.required,
+ Validators.pattern(TimeUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX),
+ ])
+ );
+ selectedRealTimeFormControl = new FormControl(
+ 'undefined',
+ Validators.compose([
+ Validators.required,
+ Validators.pattern(TimeUtils.HUMAN_REAL_TIMESTAMP_REGEX),
+ ])
+ );
+ selectedNsFormControl = new FormControl(
+ 'undefined',
+ Validators.compose([Validators.required, Validators.pattern(TimeUtils.NS_TIMESTAMP_REGEX)])
+ );
+ timestampForm = new FormGroup({
+ selectedElapsedTime: this.selectedElapsedTimeFormControl,
+ selectedRealTime: this.selectedRealTimeFormControl,
+ selectedNs: this.selectedNsFormControl,
+ });
+
+ videoUrl: SafeUrl | undefined;
+
+ private expanded = false;
+
+ TRACE_INFO = TRACE_INFO;
+
+ constructor(
+ @Inject(DomSanitizer) private sanitizer: DomSanitizer,
+ @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit() {
+ if (this.timelineData.hasTimestamps()) {
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+
+ const screenRecordingVideo = this.timelineData.getScreenRecordingVideo();
+ if (screenRecordingVideo) {
+ this.videoUrl = this.sanitizer.bypassSecurityTrustUrl(
+ URL.createObjectURL(screenRecordingVideo)
+ );
+ }
+ }
+
+ ngAfterViewInit() {
+ const height = this.collapsedTimelineRef.nativeElement.offsetHeight;
+ this.collapsedTimelineSizeChanged.emit(height);
+ }
+
+ getVideoCurrentTime() {
+ return this.timelineData.searchCorrespondingScreenRecordingTimeSeconds(
+ this.getCurrentTracePosition()
+ );
+ }
+
+ private seekTimestamp: Timestamp | undefined;
+
+ getCurrentTracePosition(): TracePosition {
+ if (this.seekTimestamp !== undefined) {
+ return TracePosition.fromTimestamp(this.seekTimestamp);
+ }
+
+ const position = this.timelineData.getCurrentPosition();
+ if (position === undefined) {
+ throw Error('A trace position should be available by the time the timeline is loaded');
+ }
+
+ return position;
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+
+ toggleExpand() {
+ this.expanded = !this.expanded;
+ this.changeDetectorRef.detectChanges();
+ }
+
+ updatePosition(position: TracePosition) {
+ this.timelineData.setPosition(position);
+ }
+
+ usingRealtime(): boolean {
+ return this.timelineData.getTimestampType() === TimestampType.REAL;
+ }
+
+ updateSeekTimestamp(timestamp: Timestamp | undefined) {
+ this.seekTimestamp = timestamp;
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+
+ private updateTimeInputValuesToCurrentTimestamp() {
+ this.selectedElapsedTimeFormControl.setValue(
+ TimeUtils.format(
+ new ElapsedTimestamp(this.getCurrentTracePosition().timestamp.getValueNs()),
+ false
+ )
+ );
+ this.selectedRealTimeFormControl.setValue(
+ TimeUtils.format(new RealTimestamp(this.getCurrentTracePosition().timestamp.getValueNs()))
+ );
+ this.selectedNsFormControl.setValue(
+ `${this.getCurrentTracePosition().timestamp.getValueNs()} ns`
+ );
+ }
+
+ isOptionDisabled(trace: TraceType) {
+ if (this.internalActiveTrace === trace) {
+ return true;
+ }
+
+ // Reached limit of options and is not a selected element
+ if (
+ (this.selectedTracesFormControl.value?.length ?? 0) >= this.MAX_SELECTED_TRACES &&
+ this.selectedTracesFormControl.value?.find((el: TraceType) => el === trace) === undefined
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ onTraceSelectionClosed() {
+ this.selectedTracesFormControl.setValue(this.selectedTraces);
+ }
+
+ applyNewTraceSelection() {
+ this.selectedTraces = this.selectedTracesFormControl.value;
+ }
+
+ @HostListener('document:keydown', ['$event'])
+ handleKeyboardEvent(event: KeyboardEvent) {
+ if (event.key === 'ArrowLeft') {
+ this.moveToPreviousEntry();
+ } else if (event.key === 'ArrowRight') {
+ this.moveToNextEntry();
+ }
+ }
+
+ hasPrevEntry(): boolean {
+ if (!this.internalActiveTrace) {
+ return false;
+ }
+ return this.timelineData.getPreviousEntryFor(this.internalActiveTrace) !== undefined;
+ }
+
+ hasNextEntry(): boolean {
+ if (!this.internalActiveTrace) {
+ return false;
+ }
+ return this.timelineData.getNextEntryFor(this.internalActiveTrace) !== undefined;
+ }
+
+ moveToPreviousEntry() {
+ if (!this.internalActiveTrace) {
+ return;
+ }
+ this.timelineData.moveToPreviousEntryFor(this.internalActiveTrace);
+ }
+
+ moveToNextEntry() {
+ if (!this.internalActiveTrace) {
+ return;
+ }
+ this.timelineData.moveToNextEntryFor(this.internalActiveTrace);
+ }
+
+ humanElapsedTimeInputChange(event: Event) {
+ if (event.type !== 'change') {
+ return;
+ }
+ const target = event.target as HTMLInputElement;
+ const timestamp = TimeUtils.parseHumanElapsed(target.value);
+ this.timelineData.setPosition(TracePosition.fromTimestamp(timestamp));
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+
+ humanRealTimeInputChanged(event: Event) {
+ if (event.type !== 'change') {
+ return;
+ }
+ const target = event.target as HTMLInputElement;
+
+ const timestamp = TimeUtils.parseHumanReal(target.value);
+ this.timelineData.setPosition(TracePosition.fromTimestamp(timestamp));
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+
+ nanosecondsInputTimeChange(event: Event) {
+ if (event.type !== 'change') {
+ return;
+ }
+ const target = event.target as HTMLInputElement;
+
+ const timestamp = new Timestamp(
+ this.timelineData.getTimestampType()!,
+ StringUtils.parseBigIntStrippingUnit(target.value)
+ );
+ this.timelineData.setPosition(TracePosition.fromTimestamp(timestamp));
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/app/components/timeline/timeline_component_stub.ts
similarity index 61%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/app/components/timeline/timeline_component_stub.ts
index bfe19ce..595e117 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/app/components/timeline/timeline_component_stub.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {TracePositionUpdateListener} from 'interfaces/trace_position_update_listener';
+import {TracePosition} from 'trace/trace_position';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class TimelineComponentStub implements TracePositionUpdateListener {
+ onTracePositionUpdate(position: TracePosition) {
+ // do nothing
+ }
+}
diff --git a/tools/winscope/src/app/components/timeline/timeline_component_test.ts b/tools/winscope/src/app/components/timeline/timeline_component_test.ts
new file mode 100644
index 0000000..7720b09
--- /dev/null
+++ b/tools/winscope/src/app/components/timeline/timeline_component_test.ts
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2022 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 {ChangeDetectionStrategy} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {MatButtonModule} from '@angular/material/button';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatIconModule} from '@angular/material/icon';
+import {MatInputModule} from '@angular/material/input';
+import {MatSelectModule} from '@angular/material/select';
+import {MatTooltipModule} from '@angular/material/tooltip';
+import {By} from '@angular/platform-browser';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {
+ MatDrawer,
+ MatDrawerContainer,
+ MatDrawerContent,
+} from 'app/components/bottomnav/bottom_drawer_component';
+import {TimelineData} from 'app/timeline_data';
+import {TracesBuilder} from 'test/unit/traces_builder';
+import {RealTimestamp} from 'trace/timestamp';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {ExpandedTimelineComponent} from './expanded_timeline_component';
+import {MiniTimelineComponent} from './mini_timeline_component';
+import {SingleTimelineComponent} from './single_timeline_component';
+import {TimelineComponent} from './timeline_component';
+
+describe('TimelineComponent', () => {
+ const time90 = new RealTimestamp(90n);
+ const time100 = new RealTimestamp(100n);
+ const time101 = new RealTimestamp(101n);
+ const time110 = new RealTimestamp(110n);
+ const time112 = new RealTimestamp(112n);
+
+ const position90 = TracePosition.fromTimestamp(time90);
+ const position100 = TracePosition.fromTimestamp(time100);
+ const position105 = TracePosition.fromTimestamp(new RealTimestamp(105n));
+ const position110 = TracePosition.fromTimestamp(time110);
+ const position112 = TracePosition.fromTimestamp(time112);
+
+ let fixture: ComponentFixture<TimelineComponent>;
+ let component: TimelineComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ FormsModule,
+ MatButtonModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatIconModule,
+ MatSelectModule,
+ MatTooltipModule,
+ ReactiveFormsModule,
+ BrowserAnimationsModule,
+ ],
+ declarations: [
+ ExpandedTimelineComponent,
+ SingleTimelineComponent,
+ MatDrawer,
+ MatDrawerContainer,
+ MatDrawerContent,
+ MiniTimelineComponent,
+ TimelineComponent,
+ ],
+ })
+ .overrideComponent(TimelineComponent, {
+ set: {changeDetection: ChangeDetectionStrategy.Default},
+ })
+ .compileComponents();
+ fixture = TestBed.createComponent(TimelineComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+
+ const timelineData = new TimelineData();
+ component.timelineData = timelineData;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('can be expanded', () => {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [time100, time110])
+ .build();
+ component.timelineData.initialize(traces, undefined);
+ fixture.detectChanges();
+
+ const button = htmlElement.querySelector(`.${component.TOGGLE_BUTTON_CLASS}`);
+ expect(button).toBeTruthy();
+
+ // initially not expanded
+ let expandedTimelineElement = fixture.debugElement.query(
+ By.directive(ExpandedTimelineComponent)
+ );
+ expect(expandedTimelineElement).toBeFalsy();
+
+ button!.dispatchEvent(new Event('click'));
+ expandedTimelineElement = fixture.debugElement.query(By.directive(ExpandedTimelineComponent));
+ expect(expandedTimelineElement).toBeTruthy();
+
+ button!.dispatchEvent(new Event('click'));
+ expandedTimelineElement = fixture.debugElement.query(By.directive(ExpandedTimelineComponent));
+ expect(expandedTimelineElement).toBeFalsy();
+ });
+
+ it('handles empty traces', () => {
+ const traces = new TracesBuilder().setEntries(TraceType.SURFACE_FLINGER, []).build();
+ component.timelineData.initialize(traces, undefined);
+ fixture.detectChanges();
+
+ // no expand button
+ const button = htmlElement.querySelector(`.${component.TOGGLE_BUTTON_CLASS}`);
+ expect(button).toBeFalsy();
+
+ // no timelines shown
+ const miniTimelineElement = fixture.debugElement.query(By.directive(MiniTimelineComponent));
+ expect(miniTimelineElement).toBeFalsy();
+
+ // error message shown
+ const errorMessageContainer = htmlElement.querySelector('.no-timestamps-msg');
+ expect(errorMessageContainer).toBeTruthy();
+ expect(errorMessageContainer!.textContent).toContain('No timeline to show!');
+ });
+
+ it('handles some empty traces', () => {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [time100])
+ .build();
+ component.timelineData.initialize(traces, undefined);
+ fixture.detectChanges();
+ });
+
+ it('processes active trace input and updates selected traces', () => {
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ expect(component.internalActiveTrace).toEqual(TraceType.SURFACE_FLINGER);
+ expect(component.selectedTraces).toEqual([TraceType.SURFACE_FLINGER]);
+
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ expect(component.internalActiveTrace).toEqual(TraceType.SURFACE_FLINGER);
+ expect(component.selectedTraces).toEqual([TraceType.SURFACE_FLINGER]);
+
+ component.activeViewTraceTypes = [TraceType.TRANSACTIONS];
+ expect(component.internalActiveTrace).toEqual(TraceType.TRANSACTIONS);
+ expect(component.selectedTraces).toEqual([TraceType.SURFACE_FLINGER, TraceType.TRANSACTIONS]);
+
+ component.activeViewTraceTypes = [TraceType.WINDOW_MANAGER];
+ expect(component.internalActiveTrace).toEqual(TraceType.WINDOW_MANAGER);
+ expect(component.selectedTraces).toEqual([
+ TraceType.SURFACE_FLINGER,
+ TraceType.TRANSACTIONS,
+ TraceType.WINDOW_MANAGER,
+ ]);
+
+ component.activeViewTraceTypes = [TraceType.PROTO_LOG];
+ expect(component.internalActiveTrace).toEqual(TraceType.PROTO_LOG);
+ expect(component.selectedTraces).toEqual([
+ TraceType.TRANSACTIONS,
+ TraceType.WINDOW_MANAGER,
+ TraceType.PROTO_LOG,
+ ]);
+ });
+
+ it('handles undefined active trace input', () => {
+ component.activeViewTraceTypes = undefined;
+ expect(component.internalActiveTrace).toBeUndefined();
+ expect(component.selectedTraces).toEqual([]);
+
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ expect(component.internalActiveTrace).toEqual(TraceType.SURFACE_FLINGER);
+ expect(component.selectedTraces).toEqual([TraceType.SURFACE_FLINGER]);
+
+ component.activeViewTraceTypes = undefined;
+ expect(component.internalActiveTrace).toEqual(TraceType.SURFACE_FLINGER);
+ expect(component.selectedTraces).toEqual([TraceType.SURFACE_FLINGER]);
+ });
+
+ it('next button disabled if no next entry', () => {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [time100, time110])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [time90, time101, time110, time112])
+ .build();
+ component.timelineData.initialize(traces, undefined);
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ component.timelineData.setPosition(position100);
+ fixture.detectChanges();
+
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+
+ const nextEntryButton = fixture.debugElement.query(By.css('#next_entry_button'));
+ expect(nextEntryButton).toBeTruthy();
+ expect(nextEntryButton.nativeElement.getAttribute('disabled')).toBeFalsy();
+
+ component.timelineData.setPosition(position90);
+ fixture.detectChanges();
+ expect(nextEntryButton.nativeElement.getAttribute('disabled')).toBeFalsy();
+
+ component.timelineData.setPosition(position110);
+ fixture.detectChanges();
+ expect(nextEntryButton.nativeElement.getAttribute('disabled')).toBeTruthy();
+
+ component.timelineData.setPosition(position112);
+ fixture.detectChanges();
+ expect(nextEntryButton.nativeElement.getAttribute('disabled')).toBeTruthy();
+ });
+
+ it('prev button disabled if no prev entry', () => {
+ const builder = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [time100, time110])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [time90, time101, time110, time112]);
+ const traces = builder.build();
+ component.timelineData.initialize(traces, undefined);
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ component.timelineData.setPosition(position100);
+ fixture.detectChanges();
+
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+ const prevEntryButton = fixture.debugElement.query(By.css('#prev_entry_button'));
+ expect(prevEntryButton).toBeTruthy();
+ expect(prevEntryButton.nativeElement.getAttribute('disabled')).toBeTruthy();
+
+ component.timelineData.setPosition(position90);
+ fixture.detectChanges();
+ expect(prevEntryButton.nativeElement.getAttribute('disabled')).toBeTruthy();
+
+ component.timelineData.setPosition(position110);
+ fixture.detectChanges();
+ expect(prevEntryButton.nativeElement.getAttribute('disabled')).toBeFalsy();
+
+ component.timelineData.setPosition(position112);
+ fixture.detectChanges();
+ expect(prevEntryButton.nativeElement.getAttribute('disabled')).toBeFalsy();
+ });
+
+ it('changes timestamp on next entry button press', () => {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [time100, time110])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [time90, time101, time110, time112])
+ .build();
+ component.timelineData.initialize(traces, undefined);
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ component.timelineData.setPosition(position100);
+ fixture.detectChanges();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+ const nextEntryButton = fixture.debugElement.query(By.css('#next_entry_button'));
+ expect(nextEntryButton).toBeTruthy();
+
+ component.timelineData.setPosition(position105);
+ fixture.detectChanges();
+ nextEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(110n);
+
+ component.timelineData.setPosition(position100);
+ fixture.detectChanges();
+ nextEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(110n);
+
+ component.timelineData.setPosition(position90);
+ fixture.detectChanges();
+ nextEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+
+ // No change when we are already on the last timestamp of the active trace
+ component.timelineData.setPosition(position110);
+ fixture.detectChanges();
+ nextEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(110n);
+
+ // No change when we are after the last entry of the active trace
+ component.timelineData.setPosition(position112);
+ fixture.detectChanges();
+ nextEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(112n);
+ });
+
+ it('changes timestamp on previous entry button press', () => {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [time100, time110])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [time90, time101, time110, time112])
+ .build();
+ component.timelineData.initialize(traces, undefined);
+ component.activeViewTraceTypes = [TraceType.SURFACE_FLINGER];
+ component.timelineData.setPosition(position100);
+ fixture.detectChanges();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+ const prevEntryButton = fixture.debugElement.query(By.css('#prev_entry_button'));
+ expect(prevEntryButton).toBeTruthy();
+
+ // In this state we are already on the first entry at timestamp 100, so
+ // there is no entry to move to before and we just don't update the timestamp
+ component.timelineData.setPosition(position105);
+ fixture.detectChanges();
+ prevEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(105n);
+
+ component.timelineData.setPosition(position110);
+ fixture.detectChanges();
+ prevEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+
+ // Active entry here should be 110 so moving back means moving to 100.
+ component.timelineData.setPosition(position112);
+ fixture.detectChanges();
+ prevEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+
+ // No change when we are already on the first timestamp of the active trace
+ component.timelineData.setPosition(position100);
+ fixture.detectChanges();
+ prevEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(100n);
+
+ // No change when we are before the first entry of the active trace
+ component.timelineData.setPosition(position90);
+ fixture.detectChanges();
+ prevEntryButton.nativeElement.click();
+ expect(component.timelineData.getCurrentPosition()?.timestamp.getValueNs()).toEqual(90n);
+ });
+});
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/app/components/timeline/utils.ts
similarity index 76%
rename from tools/winscope/src/stubs/waylandtrace.proto
rename to tools/winscope/src/app/components/timeline/utils.ts
index bfe19ce..241ad09 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/app/components/timeline/utils.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface Segment {
+ from: number;
+ to: number;
+}
diff --git a/tools/winscope/src/app/components/trace_config_component.ts b/tools/winscope/src/app/components/trace_config_component.ts
new file mode 100644
index 0000000..d8d82b9
--- /dev/null
+++ b/tools/winscope/src/app/components/trace_config_component.ts
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 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 {Component} from '@angular/core';
+import {
+ EnableConfiguration,
+ SelectionConfiguration,
+ TraceConfiguration,
+} from 'trace_collection/trace_collection_utils';
+import {TracingConfig} from 'trace_collection/tracing_config';
+
+@Component({
+ selector: 'trace-config',
+ template: `
+ <h3 class="mat-subheading-2">Trace targets</h3>
+
+ <div class="checkboxes">
+ <mat-checkbox
+ *ngFor="let traceKey of objectKeys(tracingConfig.getTraceConfig())"
+ color="primary"
+ class="trace-checkbox"
+ [checked]="tracingConfig.getTraceConfig()[traceKey].run"
+ [indeterminate]="
+ tracingConfig.getTraceConfig()[traceKey].isTraceCollection
+ ? someTraces(tracingConfig.getTraceConfig()[traceKey])
+ : false
+ "
+ (change)="changeRunTrace($event.checked, tracingConfig.getTraceConfig()[traceKey])"
+ >{{ tracingConfig.getTraceConfig()[traceKey].name }}</mat-checkbox
+ >
+ </div>
+
+ <ng-container *ngFor="let traceKey of advancedConfigTraces()">
+ <mat-divider></mat-divider>
+
+ <h3 class="mat-subheading-2">
+ {{ tracingConfig.getTraceConfig()[traceKey].name }} configuration
+ </h3>
+
+ <div
+ *ngIf="tracingConfig.getTraceConfig()[traceKey].config?.enableConfigs.length > 0"
+ class="enable-config-opt">
+ <mat-checkbox
+ *ngFor="let enableConfig of traceEnableConfigs(tracingConfig.getTraceConfig()[traceKey])"
+ color="primary"
+ class="enable-config"
+ [disabled]="
+ !tracingConfig.getTraceConfig()[traceKey].run &&
+ !tracingConfig.getTraceConfig()[traceKey].isTraceCollection
+ "
+ [(ngModel)]="enableConfig.enabled"
+ (change)="changeTraceCollectionConfig(tracingConfig.getTraceConfig()[traceKey])"
+ >{{ enableConfig.name }}</mat-checkbox
+ >
+ </div>
+
+ <div
+ *ngIf="tracingConfig.getTraceConfig()[traceKey].config?.selectionConfigs.length > 0"
+ class="selection-config-opt">
+ <mat-form-field
+ *ngFor="
+ let selectionConfig of traceSelectionConfigs(tracingConfig.getTraceConfig()[traceKey])
+ "
+ class="config-selection"
+ appearance="fill">
+ <mat-label>{{ selectionConfig.name }}</mat-label>
+
+ <mat-select
+ class="selected-value"
+ [(value)]="selectionConfig.value"
+ [disabled]="!tracingConfig.getTraceConfig()[traceKey].run">
+ <mat-option *ngFor="let option of selectionConfig.options" value="{{ option }}">{{
+ option
+ }}</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ </ng-container>
+ `,
+ styles: [
+ `
+ .checkboxes {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ column-gap: 10px;
+ }
+ .enable-config-opt,
+ .selection-config-opt {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ `,
+ ],
+})
+export class TraceConfigComponent {
+ objectKeys = Object.keys;
+ tracingConfig = TracingConfig.getInstance();
+
+ advancedConfigTraces() {
+ const advancedConfigs: string[] = [];
+ Object.keys(this.tracingConfig.getTraceConfig()).forEach((traceKey: string) => {
+ if (this.tracingConfig.getTraceConfig()[traceKey].config) {
+ advancedConfigs.push(traceKey);
+ }
+ });
+ return advancedConfigs;
+ }
+
+ traceEnableConfigs(trace: TraceConfiguration): EnableConfiguration[] {
+ if (trace.config) {
+ return trace.config.enableConfigs;
+ } else {
+ return [];
+ }
+ }
+
+ traceSelectionConfigs(trace: TraceConfiguration): SelectionConfiguration[] {
+ if (trace.config) {
+ return trace.config.selectionConfigs;
+ } else {
+ return [];
+ }
+ }
+
+ someTraces(trace: TraceConfiguration): boolean {
+ return this.traceEnableConfigs(trace).filter((trace) => trace.enabled).length > 0 && !trace.run;
+ }
+
+ changeRunTrace(run: boolean, trace: TraceConfiguration): void {
+ trace.run = run;
+ if (trace.isTraceCollection) {
+ this.traceEnableConfigs(trace).forEach((c: EnableConfiguration) => (c.enabled = run));
+ }
+ }
+
+ changeTraceCollectionConfig(trace: TraceConfiguration): void {
+ if (trace.isTraceCollection) {
+ trace.run = this.traceEnableConfigs(trace).every((c: EnableConfiguration) => c.enabled);
+ }
+ }
+}
diff --git a/tools/winscope/src/app/components/trace_config_component_test.ts b/tools/winscope/src/app/components/trace_config_component_test.ts
new file mode 100644
index 0000000..2c8775e
--- /dev/null
+++ b/tools/winscope/src/app/components/trace_config_component_test.ts
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatInputModule} from '@angular/material/input';
+import {MatSelectModule} from '@angular/material/select';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {TraceConfigComponent} from './trace_config_component';
+
+describe('TraceConfigComponent', () => {
+ let fixture: ComponentFixture<TraceConfigComponent>;
+ let component: TraceConfigComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ CommonModule,
+ MatCheckboxModule,
+ MatDividerModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatSelectModule,
+ BrowserAnimationsModule,
+ ],
+ declarations: [TraceConfigComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ fixture = TestBed.createComponent(TraceConfigComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ component.tracingConfig.setTraceConfig({
+ layers_trace: {
+ name: 'layers_trace',
+ isTraceCollection: undefined,
+ run: false,
+ config: {
+ enableConfigs: [
+ {
+ name: 'trace buffers',
+ key: 'tracebuffers',
+ enabled: true,
+ },
+ ],
+ selectionConfigs: [
+ {
+ key: 'tracinglevel',
+ name: 'tracing level',
+ options: ['verbose', 'debug', 'critical'],
+ value: 'debug',
+ },
+ ],
+ },
+ },
+ });
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('check that trace checkbox ticked on default run', () => {
+ component.tracingConfig.getTraceConfig()['layers_trace'].run = true;
+ fixture.detectChanges();
+ const box = htmlElement.querySelector('.trace-checkbox');
+ expect(box?.innerHTML).toContain('aria-checked="true"');
+ expect(box?.innerHTML).toContain('layers_trace');
+ });
+
+ it('check that trace checkbox not ticked on default run', () => {
+ component.tracingConfig.getTraceConfig()['layers_trace'].run = false;
+ fixture.detectChanges();
+ const box = htmlElement.querySelector('.trace-checkbox');
+ expect(box?.innerHTML).toContain('aria-checked="false"');
+ });
+
+ it('check that advanced configs show', () => {
+ const enable_config_opt = htmlElement.querySelector('.enable-config-opt');
+ expect(enable_config_opt).toBeTruthy();
+ expect(enable_config_opt?.innerHTML).toContain('trace buffers');
+ expect(enable_config_opt?.innerHTML).not.toContain('tracing level');
+
+ const selection_config_opt = htmlElement.querySelector('.selection-config-opt');
+ expect(selection_config_opt).toBeTruthy();
+ expect(selection_config_opt?.innerHTML).not.toContain('trace buffers');
+ expect(selection_config_opt?.innerHTML).toContain('tracing level');
+ });
+
+ it('check that changing enable config causes box to change', async () => {
+ component.tracingConfig.getTraceConfig()['layers_trace'].config!.enableConfigs[0].enabled =
+ false;
+ fixture.detectChanges();
+ await fixture.whenStable();
+ expect(htmlElement.querySelector('.enable-config')?.innerHTML).toContain(
+ 'aria-checked="false"'
+ );
+ });
+
+ it('check that changing selected config causes select to change', async () => {
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.config-selection')?.innerHTML).toContain('value="debug"');
+ component.tracingConfig.getTraceConfig()['layers_trace'].config!.selectionConfigs[0].value =
+ 'verbose';
+ fixture.detectChanges();
+ await fixture.whenStable();
+ expect(htmlElement.querySelector('.config-selection')?.innerHTML).toContain('value="verbose"');
+ });
+});
diff --git a/tools/winscope/src/app/components/trace_view_component.ts b/tools/winscope/src/app/components/trace_view_component.ts
new file mode 100644
index 0000000..7bd3bd8
--- /dev/null
+++ b/tools/winscope/src/app/components/trace_view_component.ts
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2022 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 {Component, ElementRef, EventEmitter, Inject, Input, Output} from '@angular/core';
+import {TRACE_INFO} from 'app/trace_info';
+import {PersistentStore} from 'common/persistent_store';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+
+interface Tab extends View {
+ addedToDom: boolean;
+}
+
+@Component({
+ selector: 'trace-view',
+ template: `
+ <div class="overlay">
+ <div class="draggable-container" cdkDrag cdkDragBoundary=".overlay">
+ <!--
+ TODO:
+ this draggable div is a temporary hack. We should remove the div and move the cdkDrag
+ directives into the overlay view (e.g. ViewerScreenReocordingComponent) as soon as the new
+ Angular's directive composition API is available
+ (https://github.com/angular/angular/issues/8785).
+ -->
+ </div>
+ </div>
+ <div class="header-items-wrapper">
+ <nav mat-tab-nav-bar class="tabs-navigation-bar">
+ <a
+ *ngFor="let tab of tabs"
+ mat-tab-link
+ [active]="isCurrentActiveTab(tab)"
+ (click)="onTabClick(tab)"
+ class="tab">
+ <mat-icon
+ class="icon"
+ [matTooltip]="TRACE_INFO[tab.traceType].name"
+ [style]="{color: TRACE_INFO[tab.traceType].color, marginRight: '0.5rem'}">
+ {{ TRACE_INFO[tab.traceType].icon }}
+ </mat-icon>
+ <p>
+ {{ tab.title }}
+ </p>
+ </a>
+ </nav>
+ <button
+ color="primary"
+ mat-button
+ class="save-button"
+ (click)="downloadTracesButtonClick.emit()">
+ Download all traces
+ </button>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="trace-view-content"></div>
+ `,
+ styles: [
+ `
+ .overlay {
+ z-index: 10;
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ }
+
+ .overlay .draggable-container {
+ position: absolute;
+ right: 0;
+ top: 20vh;
+ }
+
+ .header-items-wrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ }
+
+ .tabs-navigation-bar {
+ height: 100%;
+ }
+
+ .trace-view-content {
+ height: 100%;
+ overflow: auto;
+ }
+ `,
+ ],
+})
+export class TraceViewComponent {
+ @Input() viewers!: Viewer[];
+ @Input() store!: PersistentStore;
+ @Output() downloadTracesButtonClick = new EventEmitter<void>();
+ @Output() activeViewChanged = new EventEmitter<View>();
+
+ TRACE_INFO = TRACE_INFO;
+
+ private elementRef: ElementRef;
+
+ tabs: Tab[] = [];
+ private currentActiveTab: undefined | Tab;
+
+ constructor(@Inject(ElementRef) elementRef: ElementRef) {
+ this.elementRef = elementRef;
+ }
+
+ ngOnChanges() {
+ this.renderViewsTab();
+ this.renderViewsOverlay();
+ }
+
+ onTabClick(tab: Tab) {
+ this.showTab(tab);
+ }
+
+ private renderViewsTab() {
+ this.tabs = this.viewers
+ .map((viewer) => viewer.getViews())
+ .flat()
+ .filter((view) => view.type === ViewType.TAB)
+ .map((view) => {
+ return {
+ type: view.type,
+ htmlElement: view.htmlElement,
+ title: view.title,
+ addedToDom: false,
+ dependencies: view.dependencies,
+ traceType: view.traceType,
+ };
+ });
+
+ this.tabs.forEach((tab) => {
+ // TODO: setting "store" this way is a hack.
+ // Store should be part of View's interface.
+ (tab.htmlElement as any).store = this.store;
+ });
+
+ if (this.tabs.length > 0) {
+ this.showTab(this.tabs[0]);
+ }
+ }
+
+ private renderViewsOverlay() {
+ const views: View[] = this.viewers
+ .map((viewer) => viewer.getViews())
+ .flat()
+ .filter((view) => view.type === ViewType.OVERLAY);
+
+ if (views.length > 1) {
+ throw new Error(
+ 'Only one overlay view is supported. To allow more overlay views, either create more than' +
+ ' one draggable containers in this component or move the cdkDrag directives into the' +
+ " overlay view when the new Angular's directive composition API is available" +
+ ' (https://github.com/angular/angular/issues/8785).'
+ );
+ }
+
+ views.forEach((view) => {
+ view.htmlElement.style.pointerEvents = 'all';
+ const container = this.elementRef.nativeElement.querySelector(
+ '.overlay .draggable-container'
+ )!;
+ container.appendChild(view.htmlElement);
+ });
+ }
+
+ private showTab(tab: Tab) {
+ if (this.currentActiveTab) {
+ this.currentActiveTab.htmlElement.style.display = 'none';
+ }
+
+ if (!tab.addedToDom) {
+ // Workaround for b/255966194:
+ // make sure that the first time a tab content is rendered
+ // (added to the DOM) it has style.display == "". This fixes the
+ // initialization/rendering issues with cdk-virtual-scroll-viewport
+ // components inside the tab contents.
+ const traceViewContent = this.elementRef.nativeElement.querySelector('.trace-view-content')!;
+ traceViewContent.appendChild(tab.htmlElement);
+ tab.addedToDom = true;
+ } else {
+ tab.htmlElement.style.display = '';
+ }
+
+ this.currentActiveTab = tab;
+ this.activeViewChanged.emit(tab);
+ }
+
+ isCurrentActiveTab(tab: Tab) {
+ return tab === this.currentActiveTab;
+ }
+}
diff --git a/tools/winscope/src/app/components/trace_view_component_test.ts b/tools/winscope/src/app/components/trace_view_component_test.ts
new file mode 100644
index 0000000..37be18a
--- /dev/null
+++ b/tools/winscope/src/app/components/trace_view_component_test.ts
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatCardModule} from '@angular/material/card';
+import {MatDividerModule} from '@angular/material/divider';
+import {ViewerStub} from 'viewers/viewer_stub';
+import {TraceViewComponent} from './trace_view_component';
+
+describe('TraceViewComponent', () => {
+ let fixture: ComponentFixture<TraceViewComponent>;
+ let component: TraceViewComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [TraceViewComponent],
+ imports: [CommonModule, MatCardModule, MatDividerModule],
+ schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
+ }).compileComponents();
+ fixture = TestBed.createComponent(TraceViewComponent);
+ htmlElement = fixture.nativeElement;
+ component = fixture.componentInstance;
+ component.viewers = [
+ new ViewerStub('Title0', 'Content0'),
+ new ViewerStub('Title1', 'Content1'),
+ ];
+ component.ngOnChanges();
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ it('creates viewer tabs', () => {
+ const tabs: NodeList = htmlElement.querySelectorAll('.tab');
+ expect(tabs.length).toEqual(2);
+ expect(tabs.item(0)!.textContent).toContain('Title0');
+ expect(tabs.item(1)!.textContent).toContain('Title1');
+ });
+
+ it('changes active view on click', () => {
+ const getVisibleTabContents = () => {
+ const contents: HTMLElement[] = [];
+ htmlElement.querySelectorAll('.trace-view-content div').forEach((content) => {
+ if ((content as HTMLElement).style.display !== 'none') {
+ contents.push(content as HTMLElement);
+ }
+ });
+ return contents;
+ };
+
+ const tabButtons = htmlElement.querySelectorAll('.tab');
+
+ // Initially tab 0
+ fixture.detectChanges();
+ let visibleTabContents = getVisibleTabContents();
+ expect(visibleTabContents.length).toEqual(1);
+ expect(visibleTabContents[0].innerHTML).toEqual('Content0');
+
+ // Switch to tab 1
+ tabButtons[1].dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ visibleTabContents = getVisibleTabContents();
+ expect(visibleTabContents.length).toEqual(1);
+ expect(visibleTabContents[0].innerHTML).toEqual('Content1');
+
+ // Switch to tab 0
+ tabButtons[0].dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ visibleTabContents = getVisibleTabContents();
+ expect(visibleTabContents.length).toEqual(1);
+ expect(visibleTabContents[0].innerHTML).toEqual('Content0');
+ });
+
+ it('emits event on download button click', () => {
+ const spy = spyOn(component.downloadTracesButtonClick, 'emit');
+
+ const downloadButton: null | HTMLButtonElement = htmlElement.querySelector('.save-button');
+ expect(downloadButton).toBeInstanceOf(HTMLButtonElement);
+
+ downloadButton?.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalledTimes(1);
+
+ downloadButton?.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalledTimes(2);
+ });
+});
diff --git a/tools/winscope/src/app/components/upload_traces_component.ts b/tools/winscope/src/app/components/upload_traces_component.ts
new file mode 100644
index 0000000..a52692e
--- /dev/null
+++ b/tools/winscope/src/app/components/upload_traces_component.ts
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 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 {
+ ChangeDetectorRef,
+ Component,
+ EventEmitter,
+ Inject,
+ Input,
+ NgZone,
+ Output,
+} from '@angular/core';
+import {TRACE_INFO} from 'app/trace_info';
+import {TracePipeline} from 'app/trace_pipeline';
+import {ProgressListener} from 'interfaces/progress_listener';
+import {LoadedTrace} from 'trace/loaded_trace';
+import {LoadProgressComponent} from './load_progress_component';
+
+@Component({
+ selector: 'upload-traces',
+ template: `
+ <mat-card class="upload-card">
+ <mat-card-title class="title">Upload Traces</mat-card-title>
+
+ <mat-card-content
+ class="drop-box"
+ ref="drop-box"
+ (dragleave)="onFileDragOut($event)"
+ (dragover)="onFileDragIn($event)"
+ (drop)="onHandleFileDrop($event)"
+ (click)="fileDropRef.click()">
+ <input
+ id="fileDropRef"
+ hidden
+ type="file"
+ multiple
+ onclick="this.value = null"
+ #fileDropRef
+ (change)="onInputFiles($event)" />
+
+ <load-progress
+ *ngIf="isLoadingFiles"
+ [progressPercentage]="progressPercentage"
+ [message]="progressMessage">
+ </load-progress>
+
+ <mat-list
+ *ngIf="!isLoadingFiles && this.tracePipeline.getLoadedTraces().length > 0"
+ class="uploaded-files">
+ <mat-list-item *ngFor="let trace of this.tracePipeline.getLoadedTraces()">
+ <mat-icon matListIcon>
+ {{ TRACE_INFO[trace.type].icon }}
+ </mat-icon>
+
+ <p matLine>{{ TRACE_INFO[trace.type].name }}</p>
+ <p matLine *ngFor="let descriptor of trace.descriptors">{{ descriptor }}</p>
+
+ <button color="primary" mat-icon-button (click)="onRemoveTrace($event, trace)">
+ <mat-icon>close</mat-icon>
+ </button>
+ </mat-list-item>
+ </mat-list>
+
+ <div
+ *ngIf="!isLoadingFiles && tracePipeline.getLoadedTraces().length === 0"
+ class="drop-info">
+ <p class="mat-body-3 icon">
+ <mat-icon inline fontIcon="upload"></mat-icon>
+ </p>
+ <p class="mat-body-1">Drag your .winscope file(s) or click to upload</p>
+ </div>
+ </mat-card-content>
+
+ <div
+ *ngIf="!isLoadingFiles && tracePipeline.getLoadedTraces().length > 0"
+ class="trace-actions-container">
+ <button
+ color="primary"
+ mat-raised-button
+ class="load-btn"
+ (click)="onViewTracesButtonClick()">
+ View traces
+ </button>
+
+ <button color="primary" mat-stroked-button for="fileDropRef" (click)="fileDropRef.click()">
+ Upload another file
+ </button>
+
+ <button color="primary" mat-stroked-button (click)="onClearButtonClick()">Clear all</button>
+ </div>
+ </mat-card>
+ `,
+ styles: [
+ `
+ .upload-card {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ margin: 10px;
+ }
+ .drop-box {
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ border: 2px dashed var(--border-color);
+ cursor: pointer;
+ }
+ .uploaded-files {
+ flex: 400px;
+ padding: 0;
+ }
+ .drop-info {
+ flex: 400px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ pointer-events: none;
+ }
+ .drop-info p {
+ opacity: 0.6;
+ font-size: 1.2rem;
+ }
+ .drop-info .icon {
+ font-size: 3rem;
+ margin: 0;
+ }
+ .trace-actions-container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ .div-progress {
+ display: flex;
+ height: 100%;
+ flex-direction: column;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ }
+ .div-progress p {
+ opacity: 0.6;
+ }
+ .div-progress mat-icon {
+ font-size: 3rem;
+ width: unset;
+ height: unset;
+ }
+ .div-progress mat-progress-bar {
+ max-width: 250px;
+ }
+ mat-card-content {
+ flex-grow: 1;
+ }
+ `,
+ ],
+})
+export class UploadTracesComponent implements ProgressListener {
+ TRACE_INFO = TRACE_INFO;
+ isLoadingFiles = false;
+ progressMessage = '';
+ progressPercentage?: number;
+ lastUiProgressUpdateTimeMs?: number;
+
+ @Input() tracePipeline!: TracePipeline;
+ @Output() filesUploaded = new EventEmitter<File[]>();
+ @Output() viewTracesButtonClick = new EventEmitter<void>();
+
+ constructor(
+ @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef,
+ @Inject(NgZone) private ngZone: NgZone
+ ) {}
+
+ ngOnInit() {
+ this.tracePipeline.clear();
+ }
+
+ onProgressUpdate(message: string | undefined, progressPercentage: number | undefined) {
+ if (!LoadProgressComponent.canUpdateComponent(this.lastUiProgressUpdateTimeMs)) {
+ return;
+ }
+ this.isLoadingFiles = true;
+ this.progressMessage = message ? message : 'Loading...';
+ this.progressPercentage = progressPercentage;
+ this.lastUiProgressUpdateTimeMs = Date.now();
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onOperationFinished() {
+ this.isLoadingFiles = false;
+ this.lastUiProgressUpdateTimeMs = undefined;
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onInputFiles(event: Event) {
+ const files = this.getInputFiles(event);
+ this.filesUploaded.emit(files);
+ }
+
+ onViewTracesButtonClick() {
+ this.viewTracesButtonClick.emit();
+ }
+
+ onClearButtonClick() {
+ this.tracePipeline.clear();
+ this.onOperationFinished();
+ }
+
+ onFileDragIn(e: DragEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ onFileDragOut(e: DragEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ onHandleFileDrop(e: DragEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ const droppedFiles = e.dataTransfer?.files;
+ if (!droppedFiles) return;
+ this.filesUploaded.emit(Array.from(droppedFiles));
+ }
+
+ onRemoveTrace(event: MouseEvent, trace: LoadedTrace) {
+ event.preventDefault();
+ event.stopPropagation();
+ this.tracePipeline.removeTraceFile(trace.type);
+ this.onOperationFinished();
+ }
+
+ private getInputFiles(event: Event): File[] {
+ const files: FileList | null = (event?.target as HTMLInputElement)?.files;
+ if (!files || !files[0]) {
+ return [];
+ }
+ return Array.from(files);
+ }
+}
diff --git a/tools/winscope/src/app/components/upload_traces_component_test.ts b/tools/winscope/src/app/components/upload_traces_component_test.ts
new file mode 100644
index 0000000..e2d79ad
--- /dev/null
+++ b/tools/winscope/src/app/components/upload_traces_component_test.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatCardModule} from '@angular/material/card';
+import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar';
+import {TracePipeline} from 'app/trace_pipeline';
+import {UploadTracesComponent} from './upload_traces_component';
+
+describe('UploadTracesComponent', () => {
+ let fixture: ComponentFixture<UploadTracesComponent>;
+ let component: UploadTracesComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MatCardModule, MatSnackBarModule],
+ providers: [MatSnackBar],
+ declarations: [UploadTracesComponent],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(UploadTracesComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ const tracePipeline = new TracePipeline();
+ component.tracePipeline = tracePipeline;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/app/components/web_adb_component.ts b/tools/winscope/src/app/components/web_adb_component.ts
new file mode 100644
index 0000000..006d6be
--- /dev/null
+++ b/tools/winscope/src/app/components/web_adb_component.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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 {Component} from '@angular/core';
+
+@Component({
+ selector: 'web-adb',
+ template: `
+ <p class="text-icon-wrapper mat-body-1">
+ <mat-icon class="adb-icon">info</mat-icon>
+ <span class="adb-info">Add new device</span>
+ </p>
+ <p class="mat-body-1">Click the button below to follow instructions in the Chrome pop-up.</p>
+ <p class="mat-body-1">Selecting a device will kill all existing ADB connections.</p>
+ <button color="primary" class="web-select-btn" mat-raised-button>Select a device</button>
+ `,
+ styles: [
+ `
+ .text-icon-wrapper {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
+ .adb-info,
+ .web-select-btn {
+ margin-left: 5px;
+ }
+ `,
+ ],
+})
+export class WebAdbComponent {
+ adbDevice = null;
+}
diff --git a/tools/winscope/src/app/components/web_adb_component_test.ts b/tools/winscope/src/app/components/web_adb_component_test.ts
new file mode 100644
index 0000000..3774a84
--- /dev/null
+++ b/tools/winscope/src/app/components/web_adb_component_test.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatIconModule} from '@angular/material/icon';
+import {WebAdbComponent} from './web_adb_component';
+
+describe('WebAdbComponent', () => {
+ let fixture: ComponentFixture<WebAdbComponent>;
+ let component: WebAdbComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MatIconModule],
+ declarations: [WebAdbComponent],
+ }).compileComponents();
+ fixture = TestBed.createComponent(WebAdbComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('renders the info message', () => {
+ fixture.detectChanges();
+ expect(htmlElement.querySelector('.adb-info')?.innerHTML).toBe('Add new device');
+ expect(htmlElement.querySelector('.adb-icon')?.innerHTML).toBe('info');
+ });
+});
diff --git a/tools/winscope/src/app/mediator.ts b/tools/winscope/src/app/mediator.ts
new file mode 100644
index 0000000..60ee7ea
--- /dev/null
+++ b/tools/winscope/src/app/mediator.ts
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2022 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 {FileUtils, OnFile} from 'common/file_utils';
+import {BuganizerAttachmentsDownloadEmitter} from 'interfaces/buganizer_attachments_download_emitter';
+import {ProgressListener} from 'interfaces/progress_listener';
+import {RemoteBugreportReceiver} from 'interfaces/remote_bugreport_receiver';
+import {RemoteTimestampReceiver} from 'interfaces/remote_timestamp_receiver';
+import {RemoteTimestampSender} from 'interfaces/remote_timestamp_sender';
+import {Runnable} from 'interfaces/runnable';
+import {TraceDataListener} from 'interfaces/trace_data_listener';
+import {TracePositionUpdateListener} from 'interfaces/trace_position_update_listener';
+import {UserNotificationListener} from 'interfaces/user_notification_listener';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {Viewer} from 'viewers/viewer';
+import {ViewerFactory} from 'viewers/viewer_factory';
+import {TimelineData} from './timeline_data';
+import {TracePipeline} from './trace_pipeline';
+
+type CrossToolProtocolInterface = RemoteBugreportReceiver &
+ RemoteTimestampReceiver &
+ RemoteTimestampSender;
+type AbtChromeExtensionProtocolInterface = BuganizerAttachmentsDownloadEmitter & Runnable;
+
+export class Mediator {
+ private abtChromeExtensionProtocol: AbtChromeExtensionProtocolInterface;
+ private crossToolProtocol: CrossToolProtocolInterface;
+ private uploadTracesComponent?: ProgressListener;
+ private collectTracesComponent?: ProgressListener;
+ private timelineComponent?: TracePositionUpdateListener;
+ private appComponent: TraceDataListener;
+ private userNotificationListener: UserNotificationListener;
+ private storage: Storage;
+
+ private tracePipeline: TracePipeline;
+ private timelineData: TimelineData;
+ private viewers: Viewer[] = [];
+ private isChangingCurrentTimestamp = false;
+ private isTraceDataVisualized = false;
+ private lastRemoteToolTimestampReceived: Timestamp | undefined;
+ private currentProgressListener?: ProgressListener;
+
+ constructor(
+ tracePipeline: TracePipeline,
+ timelineData: TimelineData,
+ abtChromeExtensionProtocol: AbtChromeExtensionProtocolInterface,
+ crossToolProtocol: CrossToolProtocolInterface,
+ appComponent: TraceDataListener,
+ userNotificationListener: UserNotificationListener,
+ storage: Storage
+ ) {
+ this.tracePipeline = tracePipeline;
+ this.timelineData = timelineData;
+ this.abtChromeExtensionProtocol = abtChromeExtensionProtocol;
+ this.crossToolProtocol = crossToolProtocol;
+ this.appComponent = appComponent;
+ this.userNotificationListener = userNotificationListener;
+ this.storage = storage;
+
+ this.timelineData.setOnTracePositionUpdate((position) => {
+ this.onWinscopeTracePositionUpdate(position);
+ });
+
+ this.crossToolProtocol.setOnBugreportReceived(
+ async (bugreport: File, timestamp?: Timestamp) => {
+ await this.onRemoteBugreportReceived(bugreport, timestamp);
+ }
+ );
+
+ this.crossToolProtocol.setOnTimestampReceived((timestamp: Timestamp) => {
+ this.onRemoteTimestampReceived(timestamp);
+ });
+
+ this.abtChromeExtensionProtocol.setOnBuganizerAttachmentsDownloadStart(() => {
+ this.onBuganizerAttachmentsDownloadStart();
+ });
+
+ this.abtChromeExtensionProtocol.setOnBuganizerAttachmentsDownloaded(
+ async (attachments: File[]) => {
+ await this.onBuganizerAttachmentsDownloaded(attachments);
+ }
+ );
+ }
+
+ setUploadTracesComponent(uploadTracesComponent: ProgressListener | undefined) {
+ this.uploadTracesComponent = uploadTracesComponent;
+ }
+
+ setCollectTracesComponent(collectTracesComponent: ProgressListener | undefined) {
+ this.collectTracesComponent = collectTracesComponent;
+ }
+
+ setTimelineComponent(timelineComponent: TracePositionUpdateListener | undefined) {
+ this.timelineComponent = timelineComponent;
+ }
+
+ onWinscopeInitialized() {
+ this.abtChromeExtensionProtocol.run();
+ }
+
+ onWinscopeUploadNew() {
+ this.resetAppToInitialState();
+ }
+
+ async onWinscopeFilesUploaded(files: File[]) {
+ this.currentProgressListener = this.uploadTracesComponent;
+ await this.processFiles(files);
+ }
+
+ async onWinscopeFilesCollected(files: File[]) {
+ this.currentProgressListener = this.collectTracesComponent;
+ await this.processFiles(files);
+ await this.processLoadedTraceFiles();
+ }
+
+ async onWinscopeViewTracesRequest() {
+ await this.processLoadedTraceFiles();
+ }
+
+ onWinscopeTracePositionUpdate(position: TracePosition) {
+ this.executeIgnoringRecursiveTimestampNotifications(() => {
+ this.updateViewersTracePosition(position);
+
+ const timestamp = position.timestamp;
+ if (timestamp.getType() !== TimestampType.REAL) {
+ console.warn(
+ 'Cannot propagate timestamp change to remote tool.' +
+ ` Remote tool expects timestamp type ${TimestampType.REAL},` +
+ ` but Winscope wants to notify timestamp type ${timestamp.getType()}.`
+ );
+ } else {
+ this.crossToolProtocol.sendTimestamp(timestamp);
+ }
+
+ this.timelineComponent?.onTracePositionUpdate(position);
+ });
+ }
+
+ private onBuganizerAttachmentsDownloadStart() {
+ this.resetAppToInitialState();
+ this.currentProgressListener = this.uploadTracesComponent;
+ this.currentProgressListener?.onProgressUpdate('Downloading files...', undefined);
+ }
+
+ private async onBuganizerAttachmentsDownloaded(attachments: File[]) {
+ this.currentProgressListener = this.uploadTracesComponent;
+ await this.processRemoteFilesReceived(attachments);
+ }
+
+ private async onRemoteBugreportReceived(bugreport: File, timestamp?: Timestamp) {
+ this.currentProgressListener = this.uploadTracesComponent;
+ await this.processRemoteFilesReceived([bugreport]);
+ if (timestamp !== undefined) {
+ this.onRemoteTimestampReceived(timestamp);
+ }
+ }
+
+ private onRemoteTimestampReceived(timestamp: Timestamp) {
+ this.executeIgnoringRecursiveTimestampNotifications(() => {
+ this.lastRemoteToolTimestampReceived = timestamp;
+
+ if (!this.isTraceDataVisualized) {
+ return; // apply timestamp later when traces are visualized
+ }
+
+ if (this.timelineData.getTimestampType() !== timestamp.getType()) {
+ console.warn(
+ 'Cannot apply new timestamp received from remote tool.' +
+ ` Remote tool notified timestamp type ${timestamp.getType()},` +
+ ` but Winscope is accepting timestamp type ${this.timelineData.getTimestampType()}.`
+ );
+ return;
+ }
+
+ if (
+ this.timelineData.getCurrentPosition()?.timestamp.getValueNs() === timestamp.getValueNs()
+ ) {
+ return; // no timestamp change
+ }
+
+ const position = TracePosition.fromTimestamp(timestamp);
+ this.updateViewersTracePosition(position);
+ this.timelineData.setPosition(position);
+ this.timelineComponent?.onTracePositionUpdate(position); //TODO: is this redundant?
+ });
+ }
+
+ private async processRemoteFilesReceived(files: File[]) {
+ this.resetAppToInitialState();
+ await this.processFiles(files);
+ }
+
+ private async processFiles(files: File[]) {
+ let progressMessage = '';
+ const onProgressUpdate = (progressPercentage: number) => {
+ this.currentProgressListener?.onProgressUpdate(progressMessage, progressPercentage);
+ };
+
+ const traceFiles: TraceFile[] = [];
+ const onFile: OnFile = (file: File, parentArchive?: File) => {
+ traceFiles.push(new TraceFile(file, parentArchive));
+ };
+
+ progressMessage = 'Unzipping files...';
+ this.currentProgressListener?.onProgressUpdate(progressMessage, 0);
+ await FileUtils.unzipFilesIfNeeded(files, onFile, onProgressUpdate);
+
+ progressMessage = 'Parsing files...';
+ this.currentProgressListener?.onProgressUpdate(progressMessage, 0);
+ const parserErrors = await this.tracePipeline.loadTraceFiles(traceFiles, onProgressUpdate);
+ this.currentProgressListener?.onOperationFinished();
+ this.userNotificationListener?.onParserErrors(parserErrors);
+ }
+
+ private async processLoadedTraceFiles() {
+ this.currentProgressListener?.onProgressUpdate('Computing frame mapping...', undefined);
+
+ // allow the UI to update before making the main thread very busy
+ await new Promise<void>((resolve) => setTimeout(resolve, 10));
+
+ this.tracePipeline.buildTraces();
+ this.currentProgressListener?.onOperationFinished();
+
+ this.timelineData.initialize(
+ this.tracePipeline.getTraces(),
+ this.tracePipeline.getScreenRecordingVideo()
+ );
+ this.createViewers();
+ this.appComponent.onTraceDataLoaded(this.viewers);
+ this.isTraceDataVisualized = true;
+
+ if (this.lastRemoteToolTimestampReceived !== undefined) {
+ this.onRemoteTimestampReceived(this.lastRemoteToolTimestampReceived);
+ }
+ }
+
+ private createViewers() {
+ const traces = this.tracePipeline.getTraces();
+ const traceTypes = new Set<TraceType>();
+ traces.forEachTrace((trace) => {
+ traceTypes.add(trace.type);
+ });
+ this.viewers = new ViewerFactory().createViewers(traceTypes, traces, this.storage);
+
+ // Update the viewers as soon as they are created
+ const position = this.timelineData.getCurrentPosition();
+ if (position) {
+ this.onWinscopeTracePositionUpdate(position);
+ }
+ }
+
+ private updateViewersTracePosition(position: TracePosition) {
+ this.viewers.forEach((viewer) => {
+ viewer.onTracePositionUpdate(position);
+ });
+ }
+
+ private executeIgnoringRecursiveTimestampNotifications(op: () => void) {
+ if (this.isChangingCurrentTimestamp) {
+ return;
+ }
+ this.isChangingCurrentTimestamp = true;
+ try {
+ op();
+ } finally {
+ this.isChangingCurrentTimestamp = false;
+ }
+ }
+
+ private resetAppToInitialState() {
+ this.tracePipeline.clear();
+ this.timelineData.clear();
+ this.viewers = [];
+ this.isTraceDataVisualized = false;
+ this.lastRemoteToolTimestampReceived = undefined;
+ this.appComponent.onTraceDataUnloaded();
+ }
+}
diff --git a/tools/winscope/src/app/mediator_test.ts b/tools/winscope/src/app/mediator_test.ts
new file mode 100644
index 0000000..2486537
--- /dev/null
+++ b/tools/winscope/src/app/mediator_test.ts
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 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 {AbtChromeExtensionProtocolStub} from 'abt_chrome_extension/abt_chrome_extension_protocol_stub';
+import {CrossToolProtocolStub} from 'cross_tool/cross_tool_protocol_stub';
+import {ProgressListenerStub} from 'interfaces/progress_listener_stub';
+import {MockStorage} from 'test/unit/mock_storage';
+import {UnitTestUtils} from 'test/unit/utils';
+import {RealTimestamp} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TracePosition} from 'trace/trace_position';
+import {ViewerFactory} from 'viewers/viewer_factory';
+import {ViewerStub} from 'viewers/viewer_stub';
+import {AppComponentStub} from './components/app_component_stub';
+import {SnackBarOpenerStub} from './components/snack_bar_opener_stub';
+import {TimelineComponentStub} from './components/timeline/timeline_component_stub';
+import {Mediator} from './mediator';
+import {TimelineData} from './timeline_data';
+import {TracePipeline} from './trace_pipeline';
+
+describe('Mediator', () => {
+ const viewerStub = new ViewerStub('Title');
+ let inputFiles: File[];
+ let tracePipeline: TracePipeline;
+ let timelineData: TimelineData;
+ let abtChromeExtensionProtocol: AbtChromeExtensionProtocolStub;
+ let crossToolProtocol: CrossToolProtocolStub;
+ let appComponent: AppComponentStub;
+ let timelineComponent: TimelineComponentStub;
+ let uploadTracesComponent: ProgressListenerStub;
+ let collectTracesComponent: ProgressListenerStub;
+ let snackBarOpener: SnackBarOpenerStub;
+ let mediator: Mediator;
+
+ const TIMESTAMP_10 = new RealTimestamp(10n);
+ const TIMESTAMP_11 = new RealTimestamp(11n);
+ const POSITION_10 = TracePosition.fromTimestamp(TIMESTAMP_10);
+ const POSITION_11 = TracePosition.fromTimestamp(TIMESTAMP_11);
+
+ beforeAll(async () => {
+ inputFiles = [
+ await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb'),
+ await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/WindowManager.pb'),
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4'
+ ),
+ ];
+ });
+
+ beforeEach(async () => {
+ timelineComponent = new TimelineComponentStub();
+ tracePipeline = new TracePipeline();
+ timelineData = new TimelineData();
+ abtChromeExtensionProtocol = new AbtChromeExtensionProtocolStub();
+ crossToolProtocol = new CrossToolProtocolStub();
+ appComponent = new AppComponentStub();
+ timelineComponent = new TimelineComponentStub();
+ uploadTracesComponent = new ProgressListenerStub();
+ collectTracesComponent = new ProgressListenerStub();
+ snackBarOpener = new SnackBarOpenerStub();
+ mediator = new Mediator(
+ tracePipeline,
+ timelineData,
+ abtChromeExtensionProtocol,
+ crossToolProtocol,
+ appComponent,
+ snackBarOpener,
+ new MockStorage()
+ );
+ mediator.setTimelineComponent(timelineComponent);
+ mediator.setUploadTracesComponent(uploadTracesComponent);
+ mediator.setCollectTracesComponent(collectTracesComponent);
+
+ spyOn(ViewerFactory.prototype, 'createViewers').and.returnValue([viewerStub]);
+ });
+
+ it('handles uploaded traces from Winscope', async () => {
+ const spies = [
+ spyOn(uploadTracesComponent, 'onProgressUpdate'),
+ spyOn(uploadTracesComponent, 'onOperationFinished'),
+ spyOn(timelineData, 'initialize').and.callThrough(),
+ spyOn(appComponent, 'onTraceDataLoaded'),
+ spyOn(viewerStub, 'onTracePositionUpdate'),
+ ];
+
+ await mediator.onWinscopeFilesUploaded(inputFiles);
+
+ expect(uploadTracesComponent.onProgressUpdate).toHaveBeenCalled();
+ expect(uploadTracesComponent.onOperationFinished).toHaveBeenCalled();
+ expect(timelineData.initialize).not.toHaveBeenCalled();
+ expect(appComponent.onTraceDataLoaded).not.toHaveBeenCalled();
+ expect(viewerStub.onTracePositionUpdate).not.toHaveBeenCalled();
+
+ spies.forEach((spy) => {
+ spy.calls.reset();
+ });
+ await mediator.onWinscopeViewTracesRequest();
+
+ expect(uploadTracesComponent.onProgressUpdate).toHaveBeenCalled();
+ expect(uploadTracesComponent.onOperationFinished).toHaveBeenCalled();
+ expect(timelineData.initialize).toHaveBeenCalledTimes(1);
+ expect(appComponent.onTraceDataLoaded).toHaveBeenCalledOnceWith([viewerStub]);
+ // notifies viewer about current timestamp on creation
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ });
+
+ it('handles collected traces from Winscope', async () => {
+ const spies = [
+ spyOn(collectTracesComponent, 'onProgressUpdate'),
+ spyOn(collectTracesComponent, 'onOperationFinished'),
+ spyOn(timelineData, 'initialize').and.callThrough(),
+ spyOn(appComponent, 'onTraceDataLoaded'),
+ spyOn(viewerStub, 'onTracePositionUpdate'),
+ ];
+
+ await mediator.onWinscopeFilesCollected(inputFiles);
+
+ expect(collectTracesComponent.onProgressUpdate).toHaveBeenCalled();
+ expect(collectTracesComponent.onOperationFinished).toHaveBeenCalled();
+ expect(timelineData.initialize).toHaveBeenCalledTimes(1);
+ expect(appComponent.onTraceDataLoaded).toHaveBeenCalledOnceWith([viewerStub]);
+ // notifies viewer about current timestamp on creation
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ });
+
+ //TODO: test "bugreport data from cross-tool protocol" when FileUtils is fully compatible with
+ // Node.js (b/262269229). FileUtils#unzipFile() currently can't execute on Node.js.
+
+ //TODO: test "data from ABT chrome extension" when FileUtils is fully compatible with Node.js
+ // (b/262269229).
+
+ it('handles start download event from ABT chrome extension', () => {
+ spyOn(uploadTracesComponent, 'onProgressUpdate');
+ expect(uploadTracesComponent.onProgressUpdate).toHaveBeenCalledTimes(0);
+
+ abtChromeExtensionProtocol.onBuganizerAttachmentsDownloadStart();
+ expect(uploadTracesComponent.onProgressUpdate).toHaveBeenCalledTimes(1);
+ });
+
+ it('handles empty downloaded files from ABT chrome extension', async () => {
+ spyOn(uploadTracesComponent, 'onOperationFinished');
+ expect(uploadTracesComponent.onOperationFinished).toHaveBeenCalledTimes(0);
+
+ // Pass files even if empty so that the upload component will update the progress bar
+ // and display error messages
+ await abtChromeExtensionProtocol.onBuganizerAttachmentsDownloaded([]);
+ expect(uploadTracesComponent.onOperationFinished).toHaveBeenCalledTimes(1);
+ });
+
+ it('propagates trace position update from timeline data', async () => {
+ await loadTraceFiles();
+ await mediator.onWinscopeViewTracesRequest();
+
+ spyOn(viewerStub, 'onTracePositionUpdate');
+ spyOn(timelineComponent, 'onTracePositionUpdate');
+ spyOn(crossToolProtocol, 'sendTimestamp');
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+ expect(crossToolProtocol.sendTimestamp).toHaveBeenCalledTimes(0);
+
+ // notify timestamp
+ timelineData.setPosition(POSITION_10);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(crossToolProtocol.sendTimestamp).toHaveBeenCalledTimes(1);
+
+ // notify same timestamp again (ignored, no timestamp change)
+ timelineData.setPosition(POSITION_10);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(crossToolProtocol.sendTimestamp).toHaveBeenCalledTimes(1);
+
+ // notify another timestamp
+ timelineData.setPosition(POSITION_11);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(2);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(2);
+ expect(crossToolProtocol.sendTimestamp).toHaveBeenCalledTimes(2);
+ });
+
+ describe('timestamp received from remote tool', () => {
+ it('propagates trace position update', async () => {
+ await loadTraceFiles();
+ await mediator.onWinscopeViewTracesRequest();
+
+ spyOn(viewerStub, 'onTracePositionUpdate');
+ spyOn(timelineComponent, 'onTracePositionUpdate');
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+
+ // receive timestamp
+ await crossToolProtocol.onTimestampReceived(TIMESTAMP_10);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+
+ // receive same timestamp again (ignored, no timestamp change)
+ await crossToolProtocol.onTimestampReceived(TIMESTAMP_10);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+
+ // receive another
+ await crossToolProtocol.onTimestampReceived(TIMESTAMP_11);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(2);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(2);
+ });
+
+ it("doesn't propagate timestamp back to remote tool", async () => {
+ await loadTraceFiles();
+ await mediator.onWinscopeViewTracesRequest();
+
+ spyOn(viewerStub, 'onTracePositionUpdate');
+ spyOn(crossToolProtocol, 'sendTimestamp');
+
+ // receive timestamp
+ await crossToolProtocol.onTimestampReceived(TIMESTAMP_10);
+ expect(viewerStub.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+ expect(crossToolProtocol.sendTimestamp).toHaveBeenCalledTimes(0);
+ });
+
+ it('defers propagation till traces are loaded and visualized', async () => {
+ spyOn(timelineComponent, 'onTracePositionUpdate');
+
+ // keep timestamp for later
+ await crossToolProtocol.onTimestampReceived(TIMESTAMP_10);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+
+ // keep timestamp for later (replace previous one)
+ await crossToolProtocol.onTimestampReceived(TIMESTAMP_11);
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+
+ // apply timestamp
+ await loadTraceFiles();
+ await mediator.onWinscopeViewTracesRequest();
+ expect(timelineComponent.onTracePositionUpdate).toHaveBeenCalledWith(POSITION_11);
+ });
+ });
+
+ const loadTraceFiles = async () => {
+ const traceFiles = inputFiles.map((file) => new TraceFile(file));
+ const errors = await tracePipeline.loadTraceFiles(traceFiles);
+ expect(errors).toEqual([]);
+ };
+});
diff --git a/tools/winscope/src/app/timeline_data.ts b/tools/winscope/src/app/timeline_data.ts
new file mode 100644
index 0000000..ab9082b
--- /dev/null
+++ b/tools/winscope/src/app/timeline_data.ts
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils} from 'common/function_utils';
+import {TimeUtils} from 'common/time_utils';
+import {ScreenRecordingUtils} from 'trace/screen_recording_utils';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceEntry} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {assertDefined} from '../common/assert_utils';
+
+export type TracePositionCallbackType = (position: TracePosition) => void;
+export interface TimeRange {
+ from: Timestamp;
+ to: Timestamp;
+}
+
+export class TimelineData {
+ private traces = new Traces();
+ private screenRecordingVideo?: Blob;
+ private timestampType?: TimestampType;
+ private firstEntry?: TraceEntry<{}>;
+ private lastEntry?: TraceEntry<{}>;
+ private explicitlySetPosition?: TracePosition;
+ private explicitlySetSelection?: TimeRange;
+ private activeViewTraceTypes: TraceType[] = []; // dependencies of current active view
+ private onTracePositionUpdate: TracePositionCallbackType = FunctionUtils.DO_NOTHING;
+
+ initialize(traces: Traces, screenRecordingVideo: Blob | undefined) {
+ this.clear();
+
+ this.traces = traces;
+ this.screenRecordingVideo = screenRecordingVideo;
+ this.firstEntry = this.findFirstEntry();
+ this.lastEntry = this.findLastEntry();
+ this.timestampType = this.firstEntry?.getTimestamp().getType();
+
+ const position = this.getCurrentPosition();
+ if (position) {
+ this.onTracePositionUpdate(position);
+ }
+ }
+
+ setOnTracePositionUpdate(callback: TracePositionCallbackType) {
+ this.onTracePositionUpdate = callback;
+ }
+
+ getCurrentPosition(): TracePosition | undefined {
+ if (this.explicitlySetPosition) {
+ return this.explicitlySetPosition;
+ }
+ const firstActiveEntry = this.getFirstEntryOfActiveViewTraces();
+ if (firstActiveEntry) {
+ return TracePosition.fromTraceEntry(firstActiveEntry);
+ }
+ if (this.firstEntry) {
+ return TracePosition.fromTraceEntry(this.firstEntry);
+ }
+ return undefined;
+ }
+
+ setPosition(position: TracePosition | undefined) {
+ if (!this.hasTimestamps()) {
+ console.warn('Attempted to set position on traces with no timestamps/entries...');
+ return;
+ }
+
+ if (position) {
+ if (this.timestampType === undefined) {
+ throw Error('Attempted to set explicit position but no timestamp type is available');
+ }
+ if (position.timestamp.getType() !== this.timestampType) {
+ throw Error('Attempted to set explicit position with incompatible timestamp type');
+ }
+ }
+
+ this.applyOperationAndNotifyIfCurrentPositionChanged(() => {
+ this.explicitlySetPosition = position;
+ });
+ }
+
+ setActiveViewTraceTypes(types: TraceType[]) {
+ this.applyOperationAndNotifyIfCurrentPositionChanged(() => {
+ this.activeViewTraceTypes = types;
+ });
+ }
+
+ getTimestampType(): TimestampType | undefined {
+ return this.timestampType;
+ }
+
+ getFullTimeRange(): TimeRange {
+ if (!this.firstEntry || !this.lastEntry) {
+ throw Error('Trying to get full time range when there are no timestamps');
+ }
+ return {
+ from: this.firstEntry.getTimestamp(),
+ to: this.lastEntry.getTimestamp(),
+ };
+ }
+
+ getSelectionTimeRange(): TimeRange {
+ if (this.explicitlySetSelection === undefined) {
+ return this.getFullTimeRange();
+ } else {
+ return this.explicitlySetSelection;
+ }
+ }
+
+ setSelectionTimeRange(selection: TimeRange) {
+ this.explicitlySetSelection = selection;
+ }
+
+ getTraces(): Traces {
+ return this.traces;
+ }
+
+ getScreenRecordingVideo(): Blob | undefined {
+ return this.screenRecordingVideo;
+ }
+
+ searchCorrespondingScreenRecordingTimeSeconds(position: TracePosition): number | undefined {
+ const trace = this.traces.getTrace(TraceType.SCREEN_RECORDING);
+ if (!trace || trace.lengthEntries === 0) {
+ return undefined;
+ }
+
+ const firstTimestamp = trace.getEntry(0).getTimestamp();
+ const entry = TraceEntryFinder.findCorrespondingEntry(trace, position);
+ if (!entry) {
+ return undefined;
+ }
+
+ return ScreenRecordingUtils.timestampToVideoTimeSeconds(firstTimestamp, entry.getTimestamp());
+ }
+
+ hasTimestamps(): boolean {
+ return this.firstEntry !== undefined;
+ }
+
+ hasMoreThanOneDistinctTimestamp(): boolean {
+ return (
+ this.hasTimestamps() &&
+ this.firstEntry?.getTimestamp().getValueNs() !== this.lastEntry?.getTimestamp().getValueNs()
+ );
+ }
+
+ getPreviousEntryFor(type: TraceType): TraceEntry<{}> | undefined {
+ const trace = assertDefined(this.traces.getTrace(type));
+ if (trace.lengthEntries === 0) {
+ return undefined;
+ }
+
+ const currentIndex = this.findCurrentEntryFor(type)?.getIndex();
+ if (currentIndex === undefined || currentIndex === 0) {
+ return undefined;
+ }
+
+ return trace.getEntry(currentIndex - 1);
+ }
+
+ getNextEntryFor(type: TraceType): TraceEntry<{}> | undefined {
+ const trace = assertDefined(this.traces.getTrace(type));
+ if (trace.lengthEntries === 0) {
+ return undefined;
+ }
+
+ const currentIndex = this.findCurrentEntryFor(type)?.getIndex();
+ if (currentIndex === undefined) {
+ return trace.getEntry(0);
+ }
+
+ if (currentIndex + 1 >= trace.lengthEntries) {
+ return undefined;
+ }
+
+ return trace.getEntry(currentIndex + 1);
+ }
+
+ findCurrentEntryFor(type: TraceType): TraceEntry<{}> | undefined {
+ const position = this.getCurrentPosition();
+ if (!position) {
+ return undefined;
+ }
+ return TraceEntryFinder.findCorrespondingEntry(
+ assertDefined(this.traces.getTrace(type)),
+ position
+ );
+ }
+
+ moveToPreviousEntryFor(type: TraceType) {
+ const prevEntry = this.getPreviousEntryFor(type);
+ if (prevEntry !== undefined) {
+ this.setPosition(TracePosition.fromTraceEntry(prevEntry));
+ }
+ }
+
+ moveToNextEntryFor(type: TraceType) {
+ const nextEntry = this.getNextEntryFor(type);
+ if (nextEntry !== undefined) {
+ this.setPosition(TracePosition.fromTraceEntry(nextEntry));
+ }
+ }
+
+ clear() {
+ this.applyOperationAndNotifyIfCurrentPositionChanged(() => {
+ this.traces = new Traces();
+ this.firstEntry = undefined;
+ this.lastEntry = undefined;
+ this.explicitlySetPosition = undefined;
+ this.timestampType = undefined;
+ this.explicitlySetSelection = undefined;
+ this.screenRecordingVideo = undefined;
+ this.activeViewTraceTypes = [];
+ });
+ }
+
+ private findFirstEntry(): TraceEntry<{}> | undefined {
+ let first: TraceEntry<{}> | undefined = undefined;
+
+ this.traces.forEachTrace((trace) => {
+ if (trace.lengthEntries === 0) {
+ return;
+ }
+ const candidate = trace.getEntry(0);
+ if (!first || candidate.getTimestamp() < first.getTimestamp()) {
+ first = candidate;
+ }
+ });
+
+ return first;
+ }
+
+ private findLastEntry(): TraceEntry<{}> | undefined {
+ let last: TraceEntry<{}> | undefined = undefined;
+
+ this.traces.forEachTrace((trace) => {
+ if (trace.lengthEntries === 0) {
+ return;
+ }
+ const candidate = trace.getEntry(trace.lengthEntries - 1);
+ if (!last || candidate.getTimestamp() > last.getTimestamp()) {
+ last = candidate;
+ }
+ });
+
+ return last;
+ }
+
+ private getFirstEntryOfActiveViewTraces(): TraceEntry<{}> | undefined {
+ const activeEntries = this.activeViewTraceTypes
+ .map((traceType) => assertDefined(this.traces.getTrace(traceType)))
+ .filter((trace) => trace.lengthEntries > 0)
+ .map((trace) => trace.getEntry(0))
+ .sort((a, b) => {
+ return TimeUtils.compareFn(a.getTimestamp(), b.getTimestamp());
+ });
+ if (activeEntries.length === 0) {
+ return undefined;
+ }
+ return activeEntries[0];
+ }
+
+ private applyOperationAndNotifyIfCurrentPositionChanged(op: () => void) {
+ const prevPosition = this.getCurrentPosition();
+ op();
+ const currentPosition = this.getCurrentPosition();
+ if (currentPosition && (!prevPosition || !currentPosition.isEqual(prevPosition))) {
+ this.onTracePositionUpdate(currentPosition);
+ }
+ }
+}
diff --git a/tools/winscope/src/app/timeline_data_test.ts b/tools/winscope/src/app/timeline_data_test.ts
new file mode 100644
index 0000000..09ce031
--- /dev/null
+++ b/tools/winscope/src/app/timeline_data_test.ts
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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 {TracesBuilder} from 'test/unit/traces_builder';
+import {RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {TimelineData} from './timeline_data';
+
+class TracePositionUpdateListener {
+ onTracePositionUpdate(position: TracePosition) {
+ // do nothing
+ }
+}
+
+describe('TimelineData', () => {
+ let timelineData: TimelineData;
+ const positionUpdateListener = new TracePositionUpdateListener();
+
+ const timestamp10 = new Timestamp(TimestampType.REAL, 10n);
+ const timestamp11 = new Timestamp(TimestampType.REAL, 11n);
+
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp11])
+ .build();
+
+ const position10 = TracePosition.fromTraceEntry(
+ traces.getTrace(TraceType.SURFACE_FLINGER)!.getEntry(0)
+ );
+ const position11 = TracePosition.fromTraceEntry(
+ traces.getTrace(TraceType.WINDOW_MANAGER)!.getEntry(0)
+ );
+
+ beforeEach(() => {
+ timelineData = new TimelineData();
+ timelineData.setOnTracePositionUpdate((position) => {
+ positionUpdateListener.onTracePositionUpdate(position);
+ });
+ });
+
+ it('can be initialized', () => {
+ expect(timelineData.getCurrentPosition()).toBeUndefined();
+
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.getCurrentPosition()).toBeDefined();
+ });
+
+ it('uses first entry by default', () => {
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.getCurrentPosition()).toEqual(position10);
+ });
+
+ it('uses explicit position if set', () => {
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.getCurrentPosition()).toEqual(position10);
+
+ const explicitPosition = TracePosition.fromTimestamp(new RealTimestamp(1000n));
+ timelineData.setPosition(explicitPosition);
+ expect(timelineData.getCurrentPosition()).toEqual(explicitPosition);
+
+ timelineData.setActiveViewTraceTypes([TraceType.SURFACE_FLINGER]);
+ expect(timelineData.getCurrentPosition()).toEqual(explicitPosition);
+
+ timelineData.setActiveViewTraceTypes([TraceType.WINDOW_MANAGER]);
+ expect(timelineData.getCurrentPosition()).toEqual(explicitPosition);
+ });
+
+ it('sets active trace types and update current position accordingly', () => {
+ timelineData.initialize(traces, undefined);
+
+ timelineData.setActiveViewTraceTypes([]);
+ expect(timelineData.getCurrentPosition()).toEqual(position10);
+
+ timelineData.setActiveViewTraceTypes([TraceType.WINDOW_MANAGER]);
+ expect(timelineData.getCurrentPosition()).toEqual(position11);
+
+ timelineData.setActiveViewTraceTypes([TraceType.SURFACE_FLINGER]);
+ expect(timelineData.getCurrentPosition()).toEqual(position10);
+
+ timelineData.setActiveViewTraceTypes([TraceType.SURFACE_FLINGER, TraceType.WINDOW_MANAGER]);
+ expect(timelineData.getCurrentPosition()).toEqual(position10);
+ });
+
+ it('executes callback on position update', () => {
+ spyOn(positionUpdateListener, 'onTracePositionUpdate');
+ expect(positionUpdateListener.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+
+ timelineData.initialize(traces, undefined);
+ expect(positionUpdateListener.onTracePositionUpdate).toHaveBeenCalledTimes(1);
+
+ timelineData.setActiveViewTraceTypes([TraceType.WINDOW_MANAGER]);
+ expect(positionUpdateListener.onTracePositionUpdate).toHaveBeenCalledTimes(2);
+ });
+
+ it("doesn't execute callback when position doesn't change", () => {
+ timelineData.initialize(traces, undefined);
+
+ spyOn(positionUpdateListener, 'onTracePositionUpdate');
+ expect(positionUpdateListener.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+
+ timelineData.setActiveViewTraceTypes([TraceType.SURFACE_FLINGER]);
+ expect(positionUpdateListener.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+
+ timelineData.setActiveViewTraceTypes([TraceType.SURFACE_FLINGER, TraceType.WINDOW_MANAGER]);
+ expect(positionUpdateListener.onTracePositionUpdate).toHaveBeenCalledTimes(0);
+ });
+
+ it('hasTimestamps()', () => {
+ expect(timelineData.hasTimestamps()).toBeFalse();
+
+ // no trace
+ {
+ const traces = new TracesBuilder().build();
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.hasTimestamps()).toBeFalse();
+ }
+ // trace without timestamps
+ {
+ const traces = new TracesBuilder().setTimestamps(TraceType.SURFACE_FLINGER, []).build();
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.hasTimestamps()).toBeFalse();
+ }
+ // trace with timestamps
+ {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
+ .build();
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.hasTimestamps()).toBeTrue();
+ }
+ });
+
+ it('hasMoreThanOneDistinctTimestamp()', () => {
+ expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse();
+
+ // no trace
+ {
+ const traces = new TracesBuilder().build();
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse();
+ }
+ // no distinct timestamps
+ {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp10])
+ .build();
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse();
+ }
+ // distinct timestamps
+ {
+ const traces = new TracesBuilder()
+ .setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
+ .setTimestamps(TraceType.WINDOW_MANAGER, [timestamp11])
+ .build();
+ timelineData.initialize(traces, undefined);
+ expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeTrue();
+ }
+ });
+});
diff --git a/tools/winscope/src/app/trace_icons.ts b/tools/winscope/src/app/trace_icons.ts
new file mode 100644
index 0000000..d7de8fa
--- /dev/null
+++ b/tools/winscope/src/app/trace_icons.ts
@@ -0,0 +1,37 @@
+import {TraceType} from 'trace/trace_type';
+
+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 ACCESSIBILITY_ICON = 'filter_none';
+const TAG_ICON = 'details';
+const TRACE_ERROR_ICON = 'warning';
+
+interface IconMap {
+ [key: number]: string;
+}
+
+export const TRACE_ICONS: IconMap = {
+ [TraceType.ACCESSIBILITY]: ACCESSIBILITY_ICON,
+ [TraceType.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
+ [TraceType.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
+ [TraceType.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
+ [TraceType.TRANSACTIONS]: TRANSACTION_ICON,
+ [TraceType.TRANSACTIONS_LEGACY]: TRANSACTION_ICON,
+ [TraceType.WAYLAND]: WAYLAND_ICON,
+ [TraceType.WAYLAND_DUMP]: WAYLAND_ICON,
+ [TraceType.PROTO_LOG]: PROTO_LOG_ICON,
+ [TraceType.SYSTEM_UI]: SYSTEM_UI_ICON,
+ [TraceType.LAUNCHER]: LAUNCHER_ICON,
+ [TraceType.INPUT_METHOD_CLIENTS]: IME_ICON,
+ [TraceType.INPUT_METHOD_SERVICE]: IME_ICON,
+ [TraceType.INPUT_METHOD_MANAGER_SERVICE]: IME_ICON,
+ [TraceType.TAG]: TAG_ICON,
+ [TraceType.ERROR]: TRACE_ERROR_ICON,
+};
diff --git a/tools/winscope/src/app/trace_info.ts b/tools/winscope/src/app/trace_info.ts
new file mode 100644
index 0000000..af238bd
--- /dev/null
+++ b/tools/winscope/src/app/trace_info.ts
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 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 {TraceType} from 'trace/trace_type';
+
+const WINDOW_MANAGER_ICON = 'web';
+const SURFACE_FLINGER_ICON = 'layers';
+const SCREEN_RECORDING_ICON = 'videocam';
+const TRANSACTION_ICON = 'show_chart';
+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_alt';
+const ACCESSIBILITY_ICON = 'accessibility_new';
+const TAG_ICON = 'details';
+const TRACE_ERROR_ICON = 'warning';
+const EVENT_LOG_ICON = 'description';
+const TRANSITION_ICON = 'animation';
+const CUJ_ICON = 'label';
+
+interface TraceInfoMap {
+ [key: number]: {
+ name: string;
+ icon: string;
+ color: string;
+ downloadArchiveDir: string;
+ };
+}
+
+export const TRACE_INFO: TraceInfoMap = {
+ [TraceType.ACCESSIBILITY]: {
+ name: 'Accessibility',
+ icon: ACCESSIBILITY_ICON,
+ color: '#FF63B8',
+ downloadArchiveDir: 'accessibility',
+ },
+ [TraceType.WINDOW_MANAGER]: {
+ name: 'Window Manager',
+ icon: WINDOW_MANAGER_ICON,
+ color: '#AF5CF7',
+ downloadArchiveDir: 'wm',
+ },
+ [TraceType.SURFACE_FLINGER]: {
+ name: 'Surface Flinger',
+ icon: SURFACE_FLINGER_ICON,
+ color: '#4ECDE6',
+ downloadArchiveDir: 'sf',
+ },
+ [TraceType.SCREEN_RECORDING]: {
+ name: 'Screen Recording',
+ icon: SCREEN_RECORDING_ICON,
+ color: '#8A9CF9',
+ downloadArchiveDir: '',
+ },
+ [TraceType.TRANSACTIONS]: {
+ name: 'Transactions',
+ icon: TRANSACTION_ICON,
+ color: '#5BB974',
+ downloadArchiveDir: 'sf',
+ },
+ [TraceType.TRANSACTIONS_LEGACY]: {
+ name: 'Transactions Legacy',
+ icon: TRANSACTION_ICON,
+ color: '#5BB974',
+ downloadArchiveDir: 'sf',
+ },
+ [TraceType.WAYLAND]: {
+ name: 'Wayland',
+ icon: WAYLAND_ICON,
+ color: '#FDC274',
+ downloadArchiveDir: 'wayland',
+ },
+ [TraceType.WAYLAND_DUMP]: {
+ name: 'Wayland Dump',
+ icon: WAYLAND_ICON,
+ color: '#D01884',
+ downloadArchiveDir: 'wayland',
+ },
+ [TraceType.PROTO_LOG]: {
+ name: 'ProtoLog',
+ icon: PROTO_LOG_ICON,
+ color: '#40A58A',
+ downloadArchiveDir: 'protolog',
+ },
+ [TraceType.SYSTEM_UI]: {
+ name: 'System UI',
+ icon: SYSTEM_UI_ICON,
+ color: '#7A86FF',
+ downloadArchiveDir: 'sysui',
+ },
+ [TraceType.LAUNCHER]: {
+ name: 'Launcher',
+ icon: LAUNCHER_ICON,
+ color: '#137333',
+ downloadArchiveDir: 'launcher',
+ },
+ [TraceType.INPUT_METHOD_CLIENTS]: {
+ name: 'IME Clients',
+ icon: IME_ICON,
+ color: '#FA903E',
+ downloadArchiveDir: 'ime',
+ },
+ [TraceType.INPUT_METHOD_SERVICE]: {
+ name: 'IME Service',
+ icon: IME_ICON,
+ color: '#F29900',
+ downloadArchiveDir: 'ime',
+ },
+ [TraceType.INPUT_METHOD_MANAGER_SERVICE]: {
+ name: 'IME Manager Service',
+ icon: IME_ICON,
+ color: '#D93025',
+ downloadArchiveDir: 'ime',
+ },
+ [TraceType.TAG]: {
+ name: 'Tag',
+ icon: TAG_ICON,
+ color: '#4575B4',
+ downloadArchiveDir: '',
+ },
+ [TraceType.ERROR]: {
+ name: 'Error',
+ icon: TRACE_ERROR_ICON,
+ color: '#D73027',
+ downloadArchiveDir: '',
+ },
+ [TraceType.EVENT_LOG]: {
+ name: 'Event Log',
+ icon: EVENT_LOG_ICON,
+ color: '#fdd663',
+ downloadArchiveDir: 'eventlog',
+ },
+ [TraceType.WM_TRANSITION]: {
+ name: 'WM Transitions',
+ icon: TRANSITION_ICON,
+ color: '#EC407A',
+ downloadArchiveDir: 'transition',
+ },
+ [TraceType.SHELL_TRANSITION]: {
+ name: 'Shell Transitions',
+ icon: TRANSITION_ICON,
+ color: '#EC407A',
+ downloadArchiveDir: 'transition',
+ },
+ [TraceType.TRANSITION]: {
+ name: 'Transitions',
+ icon: TRANSITION_ICON,
+ color: '#EC407A',
+ downloadArchiveDir: 'transition',
+ },
+ [TraceType.CUJS]: {
+ name: 'Cujs',
+ icon: CUJ_ICON,
+ color: '#EC407A',
+ downloadArchiveDir: 'eventlog',
+ },
+};
diff --git a/tools/winscope/src/app/trace_pipeline.ts b/tools/winscope/src/app/trace_pipeline.ts
new file mode 100644
index 0000000..e1432c8
--- /dev/null
+++ b/tools/winscope/src/app/trace_pipeline.ts
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils, OnProgressUpdateType} from 'common/function_utils';
+import {ParserError, ParserFactory} from 'parsers/parser_factory';
+import {TracesParserCujs} from 'parsers/traces_parser_cujs';
+import {TracesParserTransitions} from 'parsers/traces_parser_transitions';
+import {FrameMapper} from 'trace/frame_mapper';
+import {LoadedTrace} from 'trace/loaded_trace';
+import {Parser} from 'trace/parser';
+import {TimestampType} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+
+class TracePipeline {
+ private parserFactory = new ParserFactory();
+ private parsers: Array<Parser<object>> = [];
+ private files = new Map<TraceType, TraceFile>();
+ private traces?: Traces;
+ private commonTimestampType?: TimestampType;
+
+ async loadTraceFiles(
+ traceFiles: TraceFile[],
+ onLoadProgressUpdate: OnProgressUpdateType = FunctionUtils.DO_NOTHING
+ ): Promise<ParserError[]> {
+ traceFiles = await this.filterBugreportFilesIfNeeded(traceFiles);
+ const [parsers, parserErrors] = await this.parserFactory.createParsers(
+ traceFiles,
+ onLoadProgressUpdate
+ );
+ this.parsers = parsers.map((it) => it.parser);
+
+ const tracesParsers = [
+ new TracesParserTransitions(this.parsers),
+ new TracesParserCujs(this.parsers),
+ ];
+ for (const tracesParser of tracesParsers) {
+ if (tracesParser.canProvideEntries()) {
+ this.parsers.push(tracesParser);
+ }
+ }
+
+ for (const parser of parsers) {
+ this.files.set(parser.parser.getTraceType(), parser.file);
+ }
+
+ if (this.parsers.some((it) => it.getTraceType() === TraceType.TRANSITION)) {
+ this.parsers = this.parsers.filter(
+ (it) =>
+ it.getTraceType() !== TraceType.WM_TRANSITION &&
+ it.getTraceType() !== TraceType.SHELL_TRANSITION
+ );
+ }
+
+ return parserErrors;
+ }
+
+ removeTraceFile(type: TraceType) {
+ this.parsers = this.parsers.filter((parser) => parser.getTraceType() !== type);
+ }
+
+ getLoadedFiles(): Map<TraceType, TraceFile> {
+ return this.files;
+ }
+
+ getLoadedTraces(): LoadedTrace[] {
+ return this.parsers.map(
+ (parser: Parser<object>) => new LoadedTrace(parser.getDescriptors(), parser.getTraceType())
+ );
+ }
+
+ buildTraces() {
+ const commonTimestampType = this.getCommonTimestampType();
+
+ this.traces = new Traces();
+ this.parsers.forEach((parser) => {
+ const trace = Trace.newUninitializedTrace(parser);
+ trace.init(commonTimestampType);
+ this.traces?.setTrace(parser.getTraceType(), trace);
+ });
+
+ new FrameMapper(this.traces).computeMapping();
+ }
+
+ getTraces(): Traces {
+ this.checkTracesWereBuilt();
+ return this.traces!;
+ }
+
+ getScreenRecordingVideo(): undefined | Blob {
+ const screenRecording = this.getTraces().getTrace(TraceType.SCREEN_RECORDING);
+ if (!screenRecording || screenRecording.lengthEntries === 0) {
+ return undefined;
+ }
+ return screenRecording.getEntry(0).getValue().videoData;
+ }
+
+ clear() {
+ this.parserFactory = new ParserFactory();
+ this.parsers = [];
+ this.traces = undefined;
+ this.commonTimestampType = undefined;
+ this.files = new Map<TraceType, TraceFile>();
+ }
+
+ private async filterBugreportFilesIfNeeded(files: TraceFile[]): Promise<TraceFile[]> {
+ const bugreportMainEntry = files.find((file) => file.file.name === 'main_entry.txt');
+ if (!bugreportMainEntry) {
+ return files;
+ }
+
+ const bugreportName = (await bugreportMainEntry.file.text()).trim();
+ const isBugreport = files.find((file) => file.file.name === bugreportName) !== undefined;
+ if (!isBugreport) {
+ return files;
+ }
+
+ const BUGREPORT_TRACE_DIRS = ['FS/data/misc/wmtrace/', 'FS/data/misc/perfetto-traces/'];
+ const isFileWithinBugreportTraceDir = (file: TraceFile) => {
+ for (const traceDir of BUGREPORT_TRACE_DIRS) {
+ if (file.file.name.startsWith(traceDir)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ const fileBelongsToBugreport = (file: TraceFile) =>
+ file.parentArchive === bugreportMainEntry.parentArchive;
+
+ return files.filter((file) => {
+ return isFileWithinBugreportTraceDir(file) || !fileBelongsToBugreport(file);
+ });
+ }
+
+ private getCommonTimestampType(): TimestampType {
+ if (this.commonTimestampType !== undefined) {
+ return this.commonTimestampType;
+ }
+
+ const priorityOrder = [TimestampType.REAL, TimestampType.ELAPSED];
+ for (const type of priorityOrder) {
+ if (this.parsers.every((it) => it.getTimestamps(type) !== undefined)) {
+ this.commonTimestampType = type;
+ return this.commonTimestampType;
+ }
+ }
+
+ throw Error('Failed to find common timestamp type across all traces');
+ }
+
+ private checkTracesWereBuilt() {
+ if (!this.traces) {
+ throw new Error(
+ `Can't access traces before building them. Did you forget to call '${this.buildTraces.name}'?`
+ );
+ }
+ }
+}
+
+export {TracePipeline};
diff --git a/tools/winscope/src/app/trace_pipeline_test.ts b/tools/winscope/src/app/trace_pipeline_test.ts
new file mode 100644
index 0000000..46c6701
--- /dev/null
+++ b/tools/winscope/src/app/trace_pipeline_test.ts
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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 {TracesUtils} from 'test/unit/traces_utils';
+import {UnitTestUtils} from 'test/unit/utils';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {TracePipeline} from './trace_pipeline';
+
+describe('TracePipeline', () => {
+ let tracePipeline: TracePipeline;
+
+ beforeEach(async () => {
+ tracePipeline = new TracePipeline();
+ });
+
+ it('can load valid trace files', async () => {
+ expect(tracePipeline.getLoadedTraces().length).toEqual(0);
+
+ await loadValidSfWmTraces();
+
+ expect(tracePipeline.getLoadedTraces().length).toEqual(2);
+
+ const traceEntries = TracesUtils.extractEntries(tracePipeline.getTraces());
+ expect(traceEntries.get(TraceType.WINDOW_MANAGER)?.length).toBeGreaterThan(0);
+ expect(traceEntries.get(TraceType.SURFACE_FLINGER)?.length).toBeGreaterThan(0);
+ });
+
+ it('can load bugreport and ignores non-trace dirs', async () => {
+ expect(tracePipeline.getLoadedTraces().length).toEqual(0);
+
+ // Could be any file, we just need an instance of File to be used as a fake bugreport archive
+ const bugreportArchive = await UnitTestUtils.getFixtureFile(
+ 'bugreports/bugreport_stripped.zip'
+ );
+
+ const bugreportFiles = [
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile('bugreports/main_entry.txt', 'main_entry.txt'),
+ bugreportArchive
+ ),
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile(
+ 'bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
+ 'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt'
+ ),
+ bugreportArchive
+ ),
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
+ 'FS/data/misc/wmtrace/surface_flinger.bp'
+ ),
+ bugreportArchive
+ ),
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/Transactions.pb',
+ 'FS/data/misc/wmtrace/transactions.bp'
+ ),
+ bugreportArchive
+ ),
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/WindowManager.pb',
+ 'FS/data/misc/ignored-dir/window_manager.bp'
+ ),
+ bugreportArchive
+ ),
+ ];
+
+ // Corner case:
+ // A plain trace file is loaded along the bugreport -> trace file must not be ignored
+ //
+ // Note:
+ // The even weirder corner case where two bugreports are loaded at the same time is
+ // currently not properly handled.
+ const plainTraceFile = new TraceFile(
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/InputMethodClients.pb',
+ 'would-be-ignored-if-was-part-of-bugreport/input_method_clients.pb'
+ )
+ );
+
+ const mergedFiles = bugreportFiles.concat([plainTraceFile]);
+ const errors = await tracePipeline.loadTraceFiles(mergedFiles);
+ expect(errors.length).toEqual(0);
+ tracePipeline.buildTraces();
+ const traces = tracePipeline.getTraces();
+
+ expect(traces.getTrace(TraceType.SURFACE_FLINGER)).toBeDefined();
+ expect(traces.getTrace(TraceType.TRANSACTIONS)).toBeDefined();
+ expect(traces.getTrace(TraceType.WINDOW_MANAGER)).toBeUndefined(); // ignored
+ expect(traces.getTrace(TraceType.INPUT_METHOD_CLIENTS)).toBeDefined();
+ });
+
+ it('is robust to invalid trace files', async () => {
+ const invalidTraceFiles = [
+ new TraceFile(await UnitTestUtils.getFixtureFile('winscope_homepage.png')),
+ ];
+
+ const errors = await tracePipeline.loadTraceFiles(invalidTraceFiles);
+ tracePipeline.buildTraces();
+ expect(errors.length).toEqual(1);
+ expect(tracePipeline.getLoadedTraces().length).toEqual(0);
+ });
+
+ it('is robust to mixed valid and invalid trace files', async () => {
+ expect(tracePipeline.getLoadedTraces().length).toEqual(0);
+ const files = [
+ new TraceFile(await UnitTestUtils.getFixtureFile('winscope_homepage.png')),
+ new TraceFile(await UnitTestUtils.getFixtureFile('traces/dump_WindowManager.pb')),
+ ];
+ const errors = await tracePipeline.loadTraceFiles(files);
+ tracePipeline.buildTraces();
+ expect(tracePipeline.getLoadedTraces().length).toEqual(1);
+ expect(errors.length).toEqual(1);
+ });
+
+ it('is robust to trace files with no entries', async () => {
+ const traceFilesWithNoEntries = [
+ new TraceFile(await UnitTestUtils.getFixtureFile('traces/no_entries_InputMethodClients.pb')),
+ ];
+
+ const errors = await tracePipeline.loadTraceFiles(traceFilesWithNoEntries);
+ tracePipeline.buildTraces();
+
+ expect(errors.length).toEqual(0);
+
+ expect(tracePipeline.getLoadedTraces().length).toEqual(1);
+ });
+
+ it('can remove traces', async () => {
+ await loadValidSfWmTraces();
+ expect(tracePipeline.getLoadedTraces().length).toEqual(2);
+
+ tracePipeline.removeTraceFile(TraceType.SURFACE_FLINGER);
+ tracePipeline.buildTraces();
+ expect(tracePipeline.getLoadedTraces().length).toEqual(1);
+
+ tracePipeline.removeTraceFile(TraceType.WINDOW_MANAGER);
+ tracePipeline.buildTraces();
+ expect(tracePipeline.getLoadedTraces().length).toEqual(0);
+ });
+
+ it('gets loaded trace files', async () => {
+ await loadValidSfWmTraces();
+
+ const files = tracePipeline.getLoadedTraces();
+ expect(files.length).toEqual(2);
+ expect(files[0].descriptors).toBeTruthy();
+ expect(files[0].descriptors.length).toBeGreaterThan(0);
+
+ const actualTraceTypes = new Set(files.map((file) => file.type));
+ const expectedTraceTypes = new Set([TraceType.SURFACE_FLINGER, TraceType.WINDOW_MANAGER]);
+ expect(actualTraceTypes).toEqual(expectedTraceTypes);
+ });
+
+ it('builds traces', async () => {
+ await loadValidSfWmTraces();
+ const traces = tracePipeline.getTraces();
+
+ expect(traces.getTrace(TraceType.SURFACE_FLINGER)).toBeDefined();
+ expect(traces.getTrace(TraceType.WINDOW_MANAGER)).toBeDefined();
+ });
+
+ it('gets screenrecording data', async () => {
+ const traceFiles = [
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4'
+ )
+ ),
+ ];
+ await tracePipeline.loadTraceFiles(traceFiles);
+ tracePipeline.buildTraces();
+
+ const video = tracePipeline.getScreenRecordingVideo();
+ expect(video).toBeDefined();
+ expect(video!.size).toBeGreaterThan(0);
+ });
+
+ it('can be cleared', async () => {
+ await loadValidSfWmTraces();
+ expect(tracePipeline.getLoadedTraces().length).toBeGreaterThan(0);
+
+ tracePipeline.clear();
+ expect(tracePipeline.getLoadedTraces().length).toEqual(0);
+ expect(() => {
+ tracePipeline.getTraces();
+ }).toThrow();
+ expect(() => {
+ tracePipeline.getScreenRecordingVideo();
+ }).toThrow();
+ });
+
+ it('throws if accessed before traces are built', async () => {
+ expect(() => {
+ tracePipeline.getTraces();
+ }).toThrow();
+ expect(() => {
+ tracePipeline.getScreenRecordingVideo();
+ }).toThrow();
+ });
+
+ const loadValidSfWmTraces = async () => {
+ const traceFiles = [
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb')
+ ),
+ new TraceFile(
+ await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/WindowManager.pb')
+ ),
+ ];
+
+ const errors = await tracePipeline.loadTraceFiles(traceFiles);
+ expect(errors.length).toEqual(0);
+
+ tracePipeline.buildTraces();
+ };
+});
diff --git a/tools/winscope/src/common/array_utils.ts b/tools/winscope/src/common/array_utils.ts
new file mode 100644
index 0000000..3a0250f
--- /dev/null
+++ b/tools/winscope/src/common/array_utils.ts
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 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 TypedArray =
+ | Int8Array
+ | Uint8Array
+ | Uint8ClampedArray
+ | Int16Array
+ | Uint16Array
+ | Int32Array
+ | Uint32Array
+ | Float32Array
+ | Float64Array;
+
+class ArrayUtils {
+ static equal<T>(a: T[] | TypedArray, b: T[] | TypedArray): boolean {
+ if (a.length !== b.length) {
+ return false;
+ }
+
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static searchSubarray<T>(
+ array: T[] | TypedArray,
+ subarray: T[] | TypedArray
+ ): number | undefined {
+ for (let i = 0; i + subarray.length <= array.length; ++i) {
+ let match = true;
+
+ for (let j = 0; j < subarray.length; ++j) {
+ if (array[i + j] !== subarray[j]) {
+ match = false;
+ break;
+ }
+ }
+
+ if (match) {
+ return i;
+ }
+ }
+
+ return undefined;
+ }
+
+ static binarySearchFirstGreaterOrEqual<T>(
+ values: T[] | TypedArray,
+ target: T
+ ): number | undefined {
+ if (values.length === 0) {
+ return undefined;
+ }
+
+ let low = 0;
+ let high = values.length - 1;
+
+ let result: number | undefined = undefined;
+
+ while (low <= high) {
+ const mid = (low + high) >> 1;
+
+ if (values[mid] < target) {
+ low = mid + 1;
+ } else if (values[mid] > target) {
+ if (result === undefined || result > mid) {
+ result = mid;
+ }
+ high = mid - 1;
+ } else {
+ result = mid;
+ high = mid - 1;
+ }
+ }
+
+ return result;
+ }
+
+ static binarySearchFirstGreater<T>(values: T[] | TypedArray, target: T): number | undefined {
+ if (values.length === 0) {
+ return undefined;
+ }
+
+ let low = 0;
+ let high = values.length - 1;
+
+ let result: number | undefined = undefined;
+
+ while (low <= high) {
+ const mid = (low + high) >> 1;
+
+ if (values[mid] < target) {
+ low = mid + 1;
+ } else if (values[mid] > target) {
+ if (result === undefined || result > mid) {
+ result = mid;
+ }
+ high = mid - 1;
+ } else {
+ low = mid + 1;
+ }
+ }
+
+ return result;
+ }
+
+ static toUintLittleEndian(buffer: Uint8Array, start: number, end: number): bigint {
+ let result = 0n;
+ for (let i = end - 1; i >= start; --i) {
+ result *= 256n;
+ result += BigInt(buffer[i]);
+ }
+ return result;
+ }
+
+ static toIntLittleEndian(buffer: Uint8Array, start: number, end: number): bigint {
+ const numOfBits = BigInt(Math.max(0, 8 * (end - start)));
+ if (numOfBits <= 0n) {
+ return 0n;
+ }
+
+ let result = ArrayUtils.toUintLittleEndian(buffer, start, end);
+ const maxSignedValue = 2n ** (numOfBits - 1n) - 1n;
+ if (result > maxSignedValue) {
+ const valuesRange = 2n ** numOfBits;
+ result -= valuesRange;
+ }
+
+ return result;
+ }
+}
+
+export {ArrayUtils};
diff --git a/tools/winscope/src/common/array_utils_test.ts b/tools/winscope/src/common/array_utils_test.ts
new file mode 100644
index 0000000..611a4e9
--- /dev/null
+++ b/tools/winscope/src/common/array_utils_test.ts
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 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 {ArrayUtils} from './array_utils';
+
+describe('ArrayUtils', () => {
+ it('equal', () => {
+ expect(ArrayUtils.equal([], [1])).toBeFalse();
+ expect(ArrayUtils.equal([1], [])).toBeFalse();
+
+ expect(ArrayUtils.equal([], [])).toBeTrue();
+ expect(ArrayUtils.equal([undefined], [undefined])).toBeTrue();
+ expect(ArrayUtils.equal([1, 2, 3], [1, 2, 3])).toBeTrue();
+
+ expect(ArrayUtils.equal([], new Uint8Array(1))).toBeFalse();
+ expect(ArrayUtils.equal([1], new Uint8Array(1))).toBeFalse();
+
+ expect(ArrayUtils.equal([], new Uint8Array())).toBeTrue();
+ expect(ArrayUtils.equal([], new Uint8Array())).toBeTrue();
+ expect(ArrayUtils.equal([1, 2, 3], new Uint8Array([1, 2, 3]))).toBeTrue();
+
+ expect(ArrayUtils.equal(new Uint8Array([]), new Uint8Array([1]))).toBeFalse();
+ expect(ArrayUtils.equal(new Uint8Array([1]), new Uint8Array([]))).toBeFalse();
+
+ expect(ArrayUtils.equal(new Uint8Array([]), new Uint8Array([]))).toBeTrue();
+ expect(ArrayUtils.equal(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]))).toBeTrue();
+ });
+
+ it('searchSubarray', () => {
+ expect(ArrayUtils.searchSubarray([], [0])).toEqual(undefined);
+ expect(ArrayUtils.searchSubarray([], [])).toEqual(0);
+ expect(ArrayUtils.searchSubarray([0], [])).toEqual(0);
+
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [-1])).toEqual(undefined);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [])).toEqual(0);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [0])).toEqual(0);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [1])).toEqual(1);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [2])).toEqual(2);
+
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [0, 1])).toEqual(0);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [1, 2])).toEqual(1);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [2])).toEqual(2);
+ expect(ArrayUtils.searchSubarray([0, 1, 2], [2, 3])).toEqual(undefined);
+ });
+
+ it('binarySearchFirstGreaterOrEqual', () => {
+ // no match
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([], 9)).toBeUndefined();
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([8], 9)).toBeUndefined();
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([7, 8], 9)).toBeUndefined();
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([6, 7, 8], 9)).toBeUndefined();
+
+ // match (greater)
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([6], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([6, 7], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 6], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 6, 7, 8], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 6, 7, 8], 5)).toEqual(2);
+
+ // match (equal)
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 6], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 5], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 5], 5)).toEqual(2);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 5, 6], 5)).toEqual(2);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 5, 6, 7], 5)).toEqual(2);
+
+ // match (equal with repeated values)
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 5], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 5, 5], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 5, 5, 5], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 5, 5, 6], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 4, 5, 5, 5, 6], 5)).toEqual(2);
+ expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 4, 4, 5, 5, 5, 5, 6], 5)).toEqual(3);
+ });
+
+ it('binarySearchFirstGreater', () => {
+ // no match
+ expect(ArrayUtils.binarySearchFirstGreater([], 9)).toBeUndefined();
+ expect(ArrayUtils.binarySearchFirstGreater([8], 9)).toBeUndefined();
+ expect(ArrayUtils.binarySearchFirstGreater([7, 8], 9)).toBeUndefined();
+ expect(ArrayUtils.binarySearchFirstGreater([6, 7, 8], 9)).toBeUndefined();
+
+ // match
+ expect(ArrayUtils.binarySearchFirstGreater([6], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreater([6, 7], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreater([4, 6], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreater([4, 6, 7, 8], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreater([3, 4, 6, 7, 8], 5)).toEqual(2);
+
+ // match (ignore equal)
+ expect(ArrayUtils.binarySearchFirstGreater([5], 5)).toEqual(undefined);
+ expect(ArrayUtils.binarySearchFirstGreater([5, 6], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreater([4, 5, 6], 5)).toEqual(2);
+ expect(ArrayUtils.binarySearchFirstGreater([3, 4, 5, 6], 5)).toEqual(3);
+ expect(ArrayUtils.binarySearchFirstGreater([3, 4, 5, 6, 7], 5)).toEqual(3);
+
+ // match (with repeated values)
+ expect(ArrayUtils.binarySearchFirstGreater([6, 6], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreater([6, 6, 6], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreater([6, 6, 6, 6], 5)).toEqual(0);
+ expect(ArrayUtils.binarySearchFirstGreater([5, 6, 6, 7], 5)).toEqual(1);
+ expect(ArrayUtils.binarySearchFirstGreater([5, 5, 6, 6, 6, 7], 5)).toEqual(2);
+ expect(ArrayUtils.binarySearchFirstGreater([5, 5, 5, 6, 6, 6, 6, 7], 5)).toEqual(3);
+ });
+
+ it('toUintLittleEndian', () => {
+ const buffer = new Uint8Array([0, 0, 1, 1]);
+
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, -1)).toEqual(0n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, 0)).toEqual(0n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 1, 1)).toEqual(0n);
+
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 0, 1)).toEqual(0n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 1, 2)).toEqual(1n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 2, 3)).toEqual(255n);
+
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x00]), 0, 2)).toEqual(0n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x01, 0x00]), 0, 2)).toEqual(1n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01]), 0, 2)).toEqual(256n);
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, 2)).toEqual(0xffffn);
+
+ expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff]), 0, 4)).toEqual(
+ 0xffffffffn
+ );
+
+ expect(
+ ArrayUtils.toUintLittleEndian(
+ new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
+ 0,
+ 8
+ )
+ ).toEqual(0xffffffffffffffffn);
+
+ expect(
+ ArrayUtils.toUintLittleEndian(
+ new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
+ 0,
+ 9
+ )
+ ).toEqual(0xffffffffffffffffffn);
+ });
+
+ it('toIntLittleEndian', () => {
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, -1)).toEqual(0n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, 0)).toEqual(0n);
+
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00]), 0, 1)).toEqual(0n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01]), 0, 1)).toEqual(1n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x7f]), 0, 1)).toEqual(127n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x80]), 0, 1)).toEqual(-128n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, 1)).toEqual(-1n);
+
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0x7f]), 0, 2)).toEqual(32767n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x80]), 0, 2)).toEqual(-32768n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x80]), 0, 2)).toEqual(-32767n);
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff]), 0, 2)).toEqual(-1n);
+
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0x7f]), 0, 4)).toEqual(
+ 0x7fffffffn
+ );
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x80]), 0, 4)).toEqual(
+ -0x80000000n
+ );
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x80]), 0, 4)).toEqual(
+ -0x7fffffffn
+ );
+ expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff]), 0, 4)).toEqual(
+ -1n
+ );
+
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]),
+ 0,
+ 8
+ )
+ ).toEqual(0x7fffffffffffffffn);
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]),
+ 0,
+ 8
+ )
+ ).toEqual(-0x8000000000000000n);
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]),
+ 0,
+ 8
+ )
+ ).toEqual(-0x7fffffffffffffffn);
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
+ 0,
+ 8
+ )
+ ).toEqual(-1n);
+
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]),
+ 0,
+ 9
+ )
+ ).toEqual(0x7fffffffffffffffffn);
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]),
+ 0,
+ 9
+ )
+ ).toEqual(-0x800000000000000000n);
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]),
+ 0,
+ 9
+ )
+ ).toEqual(-0x7fffffffffffffffffn);
+ expect(
+ ArrayUtils.toIntLittleEndian(
+ new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
+ 0,
+ 9
+ )
+ ).toEqual(-1n);
+ });
+});
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/common/assert_utils.ts
similarity index 68%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/common/assert_utils.ts
index bfe19ce..bc20385 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/common/assert_utils.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export function assertDefined<A>(value: A | null | undefined): A {
+ if (value === undefined || value === null) {
+ throw new Error(`Expected value, but found '${value}'`);
+ }
+ return value;
+}
diff --git a/tools/winscope/src/common/file_utils.ts b/tools/winscope/src/common/file_utils.ts
new file mode 100644
index 0000000..ac2b688
--- /dev/null
+++ b/tools/winscope/src/common/file_utils.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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';
+import {FunctionUtils, OnProgressUpdateType} from './function_utils';
+
+export type OnFile = (file: File, parentArchive: File | undefined) => void;
+
+class FileUtils {
+ static getFileExtension(file: File) {
+ const split = file.name.split('.');
+ if (split.length > 1) {
+ return split.pop();
+ }
+ return undefined;
+ }
+
+ static removeDirFromFileName(name: string) {
+ if (name.includes('/')) {
+ const startIndex = name.lastIndexOf('/') + 1;
+ return name.slice(startIndex);
+ } else {
+ return name;
+ }
+ }
+
+ static async createZipArchive(files: File[]): Promise<Blob> {
+ const zip = new JSZip();
+ for (let i = 0; i < files.length; i++) {
+ const file = files[i];
+ const blob = await file.arrayBuffer();
+ zip.file(file.name, blob);
+ }
+ return await zip.generateAsync({type: 'blob'});
+ }
+
+ static async unzipFile(
+ file: File,
+ onProgressUpdate: OnProgressUpdateType = FunctionUtils.DO_NOTHING
+ ): Promise<File[]> {
+ const unzippedFiles: File[] = [];
+ const zip = new JSZip();
+ const content = await zip.loadAsync(file);
+
+ const filenames = Object.keys(content.files);
+ for (const [index, filename] of filenames.entries()) {
+ const file = content.files[filename];
+ if (file.dir) {
+ // Ignore directories
+ continue;
+ } else {
+ const fileBlob = await file.async('blob');
+ const unzippedFile = new File([fileBlob], filename);
+ unzippedFiles.push(unzippedFile);
+ }
+
+ onProgressUpdate((100 * (index + 1)) / filenames.length);
+ }
+
+ return unzippedFiles;
+ }
+
+ static async unzipFilesIfNeeded(
+ files: File[],
+ onFile: OnFile,
+ onProgressUpdate: OnProgressUpdateType = FunctionUtils.DO_NOTHING
+ ) {
+ for (let i = 0; i < files.length; i++) {
+ const file = files[i];
+
+ const onSubprogressUpdate = (subPercentage: number) => {
+ const percentage = (100 * i) / files.length + subPercentage / files.length;
+ onProgressUpdate(percentage);
+ };
+
+ if (FileUtils.isZipFile(file)) {
+ const unzippedFile = await FileUtils.unzipFile(file, onSubprogressUpdate);
+ unzippedFile.forEach((unzippedFile) => onFile(unzippedFile, file));
+ } else {
+ onFile(file, undefined);
+ }
+ }
+ }
+
+ static isZipFile(file: File) {
+ return FileUtils.getFileExtension(file) === 'zip';
+ }
+}
+
+export {FileUtils};
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/common/function_utils.ts
similarity index 64%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/common/function_utils.ts
index bfe19ce..c5c2b3f 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/common/function_utils.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+export type OnProgressUpdateType = (percentage: number) => void;
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class FunctionUtils {
+ static readonly DO_NOTHING = () => {
+ // do nothing
+ };
+
+ static readonly DO_NOTHING_ASYNC = (): Promise<void> => {
+ return Promise.resolve();
+ };
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/common/global_config.ts
similarity index 65%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/common/global_config.ts
index bfe19ce..7325b34 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/common/global_config.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+export type Schema = Omit<GlobalConfig, 'set'>;
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+class GlobalConfig {
+ readonly MODE: 'DEV' | 'PROD' = 'PROD' as const;
+
+ set(config: Schema) {
+ Object.assign(this, config);
+ }
+}
+
+export const globalConfig = new GlobalConfig();
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/common/persistent_store.ts
similarity index 70%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/common/persistent_store.ts
index bfe19ce..e9270e5 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/common/persistent_store.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
+export class PersistentStore {
+ add(key: string, value: string) {
+ localStorage.setItem(key, value);
+ }
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+ get(key: string) {
+ return localStorage.getItem(key);
+ }
+}
diff --git a/tools/winscope/src/common/persistent_store_proxy.ts b/tools/winscope/src/common/persistent_store_proxy.ts
new file mode 100644
index 0000000..7f909c0
--- /dev/null
+++ b/tools/winscope/src/common/persistent_store_proxy.ts
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022, 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 class PersistentStoreProxy {
+ static new<T extends object>(key: string, defaultState: T, storage: Storage): T {
+ const storedState = JSON.parse(storage.getItem(key) ?? '{}');
+ const currentState = mergeDeep({}, structuredClone(defaultState));
+ mergeDeepKeepingStructure(currentState, storedState);
+ return wrapWithPersistentStoreProxy(key, currentState, storage) as T;
+ }
+}
+
+function wrapWithPersistentStoreProxy(
+ storeKey: string,
+ object: object,
+ storage: Storage,
+ baseObject: object = object
+): object {
+ const updatableProps: string[] = [];
+
+ for (const [key, value] of Object.entries(object)) {
+ if (typeof value === 'string' || typeof value === 'boolean' || value === undefined) {
+ if (!Array.isArray(object)) {
+ updatableProps.push(key);
+ }
+ } else {
+ (object as any)[key] = wrapWithPersistentStoreProxy(storeKey, value, storage, baseObject);
+ }
+ }
+
+ const proxyObj = new Proxy(object, {
+ set: (target, prop, newValue) => {
+ if (typeof prop === 'symbol') {
+ throw Error("Can't use symbol keys only strings");
+ }
+ if (Array.isArray(target) && typeof prop === 'number') {
+ target[prop] = newValue;
+ storage.setItem(storeKey, JSON.stringify(baseObject));
+ return true;
+ }
+ if (!Array.isArray(target) && updatableProps.includes(prop)) {
+ (target as any)[prop] = newValue;
+ storage.setItem(storeKey, JSON.stringify(baseObject));
+ return true;
+ }
+ throw Error(
+ `Object property '${prop}' is not updatable. Can only update leaf keys: [${updatableProps}]`
+ );
+ },
+ });
+
+ return proxyObj;
+}
+
+function isObject(item: any): boolean {
+ return item && typeof item === 'object' && !Array.isArray(item);
+}
+
+/**
+ * Merge sources into the target keeping the structure of the target.
+ * @param target the object we mutate by merging the data from source into, but keep the object structure of
+ * @param source the object we merge into target
+ * @return the mutated target object
+ */
+function mergeDeepKeepingStructure(target: any, source: any): any {
+ if (isObject(target) && isObject(source)) {
+ for (const key in target) {
+ if (source[key] === undefined) {
+ continue;
+ }
+
+ if (isObject(target[key]) && isObject(source[key])) {
+ mergeDeepKeepingStructure(target[key], source[key]);
+ continue;
+ }
+
+ if (!isObject(target[key]) && !isObject(source[key])) {
+ Object.assign(target, {[key]: source[key]});
+ continue;
+ }
+ }
+ }
+
+ return target;
+}
+
+function mergeDeep(target: any, ...sources: any): any {
+ if (!sources.length) return target;
+ const source = sources.shift();
+
+ if (isObject(target) && isObject(source)) {
+ for (const key in source) {
+ if (isObject(source[key])) {
+ if (!target[key]) Object.assign(target, {[key]: {}});
+ mergeDeep(target[key], source[key]);
+ } else {
+ Object.assign(target, {[key]: source[key]});
+ }
+ }
+ }
+
+ return mergeDeep(target, ...sources);
+}
diff --git a/tools/winscope/src/common/persistent_store_proxy_test.ts b/tools/winscope/src/common/persistent_store_proxy_test.ts
new file mode 100644
index 0000000..0a1fd23
--- /dev/null
+++ b/tools/winscope/src/common/persistent_store_proxy_test.ts
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 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 {MockStorage} from 'test/unit/mock_storage';
+import {PersistentStoreProxy} from './persistent_store_proxy';
+
+describe('PersistentStoreObject', () => {
+ it('uses defaults when no store is available', () => {
+ const mockStorage = new MockStorage();
+
+ const defaultValues = {
+ key1: 'value',
+ key2: true,
+ };
+ const storeObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+
+ expect(storeObject['key1']).toBe('value');
+ expect(storeObject['key2']).toBe(true);
+ });
+
+ it('can update properties', () => {
+ const mockStorage = new MockStorage();
+
+ const defaultValues = {
+ key1: 'value',
+ key2: true,
+ };
+ const storeObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+
+ storeObject['key1'] = 'someOtherValue';
+ storeObject['key2'] = false;
+ expect(storeObject['key1']).toBe('someOtherValue');
+ expect(storeObject['key2']).toBe(false);
+ });
+
+ it('uses explicitly set store data', () => {
+ const mockStorage = new MockStorage();
+
+ const defaultValues = {
+ key1: 'value',
+ key2: true,
+ };
+ const storeObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+ storeObject['key1'] = 'someOtherValue';
+ storeObject['key2'] = false;
+
+ const newStoreObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+ expect(newStoreObject['key1']).toBe('someOtherValue');
+ expect(newStoreObject['key2']).toBe(false);
+ });
+
+ it('uses default values if not explicitly set', () => {
+ const mockStorage = new MockStorage();
+
+ const defaultValues = {
+ key1: 'value',
+ key2: true,
+ };
+ const storeObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+ expect(storeObject['key1']).toBe('value');
+ expect(storeObject['key2']).toBe(true);
+
+ const newDefaultValues = {
+ key1: 'someOtherValue',
+ key2: false,
+ };
+ const newStoreObject = PersistentStoreProxy.new('storeKey', newDefaultValues, mockStorage);
+ expect(newStoreObject['key1']).toBe('someOtherValue');
+ expect(newStoreObject['key2']).toBe(false);
+ });
+
+ it("can't update non leaf configs", () => {
+ const mockStorage = new MockStorage();
+
+ const defaultValues = {
+ key1: 'value',
+ key2: {
+ key3: true,
+ },
+ };
+ const storeObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+ expect(() => (storeObject['key2'] = {key3: false})).toThrow();
+ });
+
+ it('can get nested configs', () => {
+ const mockStorage = new MockStorage();
+
+ const defaultValues = {
+ key1: 'value',
+ key2: {
+ key3: true,
+ },
+ };
+ const storeObject = PersistentStoreProxy.new('storeKey', defaultValues, mockStorage);
+ expect(storeObject['key2']['key3']).toBe(true);
+ });
+
+ it('can update schema', () => {
+ const mockStorage = new MockStorage();
+
+ const schema1 = {
+ key1: 'value1',
+ key2: {
+ key3: true,
+ },
+ };
+ const storeObject1 = PersistentStoreProxy.new('storeKey', schema1, mockStorage);
+ expect(storeObject1['key1']).toBe('value1');
+ expect(storeObject1['key2']['key3']).toBe(true);
+
+ // Change from default value to ensure we update the storage
+ storeObject1['key1'] = 'someOtherValue';
+ storeObject1['key2']['key3'] = false;
+
+ const schema2 = {
+ key1: {
+ key3: 'value2',
+ },
+ key2: true,
+ };
+ const storeObject2 = PersistentStoreProxy.new('storeKey', schema2, mockStorage);
+ expect(storeObject2['key1']['key3']).toBe('value2');
+ expect(storeObject2['key2']).toBe(true);
+ });
+});
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/common/string_utils.ts
similarity index 63%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/common/string_utils.ts
index bfe19ce..98192ca 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/common/string_utils.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+class StringUtils {
+ static parseBigIntStrippingUnit(s: string): bigint {
+ const match = s.match(/^\s*(-?\d+)\D*.*$/);
+ if (!match) {
+ throw new Error(`Cannot parse '${s}' as bigint`);
+ }
+ return BigInt(match[1]);
+ }
+}
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export {StringUtils};
diff --git a/tools/winscope/src/common/string_utils_test.ts b/tools/winscope/src/common/string_utils_test.ts
new file mode 100644
index 0000000..2536c44
--- /dev/null
+++ b/tools/winscope/src/common/string_utils_test.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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 {StringUtils} from './string_utils';
+
+describe('StringUtils', () => {
+ it('parses bigint', () => {
+ expect(StringUtils.parseBigIntStrippingUnit('-10')).toEqual(-10n);
+ expect(StringUtils.parseBigIntStrippingUnit('-10 unit')).toEqual(-10n);
+ expect(StringUtils.parseBigIntStrippingUnit('-10unit')).toEqual(-10n);
+ expect(StringUtils.parseBigIntStrippingUnit(' -10 unit ')).toEqual(-10n);
+
+ expect(StringUtils.parseBigIntStrippingUnit('0')).toEqual(0n);
+ expect(StringUtils.parseBigIntStrippingUnit('0 unit')).toEqual(0n);
+ expect(StringUtils.parseBigIntStrippingUnit('0unit')).toEqual(0n);
+ expect(StringUtils.parseBigIntStrippingUnit(' 0 unit ')).toEqual(0n);
+
+ expect(StringUtils.parseBigIntStrippingUnit('10')).toEqual(10n);
+ expect(StringUtils.parseBigIntStrippingUnit('10 unit')).toEqual(10n);
+ expect(StringUtils.parseBigIntStrippingUnit('10unit')).toEqual(10n);
+ expect(StringUtils.parseBigIntStrippingUnit(' 10 unit ')).toEqual(10n);
+
+ expect(() => StringUtils.parseBigIntStrippingUnit('invalid')).toThrow();
+ expect(() => StringUtils.parseBigIntStrippingUnit('invalid 10 unit')).toThrow();
+ });
+});
diff --git a/tools/winscope/src/common/time_utils.ts b/tools/winscope/src/common/time_utils.ts
new file mode 100644
index 0000000..42d6cf1
--- /dev/null
+++ b/tools/winscope/src/common/time_utils.ts
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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 {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+
+export class TimeUtils {
+ static compareFn(a: Timestamp, b: Timestamp): number {
+ if (a.getType() !== b.getType()) {
+ throw new Error('Attempted to compare two timestamps with different type');
+ }
+ return Number(a.getValueNs() - b.getValueNs());
+ }
+
+ static format(timestamp: Timestamp, hideNs = false): string {
+ switch (timestamp.getType()) {
+ case TimestampType.ELAPSED: {
+ return TimeUtils.nanosecondsToHumanElapsed(timestamp.getValueNs(), hideNs);
+ }
+ case TimestampType.REAL: {
+ return TimeUtils.nanosecondsToHumanReal(timestamp.getValueNs(), hideNs);
+ }
+ default: {
+ throw Error('Unhandled timestamp type');
+ }
+ }
+ }
+
+ private static nanosecondsToHumanElapsed(timestampNanos: number | bigint, hideNs = true): string {
+ timestampNanos = BigInt(timestampNanos);
+ const units = TimeUtils.units;
+
+ let leftNanos = timestampNanos;
+ const parts: Array<{value: bigint; unit: string}> = units
+ .slice()
+ .reverse()
+ .map(({nanosInUnit, unit}) => {
+ let amountOfUnit = BigInt(0);
+ if (leftNanos >= nanosInUnit) {
+ amountOfUnit = leftNanos / BigInt(nanosInUnit);
+ }
+ leftNanos = leftNanos % BigInt(nanosInUnit);
+ return {value: amountOfUnit, unit};
+ });
+
+ if (hideNs) {
+ parts.pop();
+ }
+
+ // Remove all 0ed units at start
+ while (parts.length > 1 && parts[0].value === 0n) {
+ parts.shift();
+ }
+
+ return parts.map((part) => `${part.value}${part.unit}`).join('');
+ }
+
+ private static nanosecondsToHumanReal(timestampNanos: number | bigint, hideNs = true): string {
+ timestampNanos = BigInt(timestampNanos);
+ const ms = timestampNanos / 1000000n;
+ const extraNanos = timestampNanos % 1000000n;
+ const formattedTimestamp = new Date(Number(ms)).toISOString().replace('Z', '');
+
+ if (hideNs) {
+ return formattedTimestamp;
+ } else {
+ return `${formattedTimestamp}${extraNanos.toString().padStart(6, '0')}`;
+ }
+ }
+
+ static parseHumanElapsed(timestampHuman: string): Timestamp {
+ if (!TimeUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX.test(timestampHuman)) {
+ throw Error('Invalid elapsed timestamp format');
+ }
+
+ const units = TimeUtils.units;
+
+ const usedUnits = timestampHuman.split(/[0-9]+/).filter((it) => it !== '');
+ const usedValues = timestampHuman
+ .split(/[a-z]+/)
+ .filter((it) => it !== '')
+ .map((it) => Math.floor(Number(it)));
+
+ let ns = BigInt(0);
+
+ for (let i = 0; i < usedUnits.length; i++) {
+ const unit = usedUnits[i];
+ const value = usedValues[i];
+ const unitData = units.find((it) => it.unit === unit)!;
+ ns += BigInt(unitData.nanosInUnit) * BigInt(value);
+ }
+
+ return new ElapsedTimestamp(ns);
+ }
+
+ static parseHumanReal(timestampHuman: string): Timestamp {
+ if (!TimeUtils.HUMAN_REAL_TIMESTAMP_REGEX.test(timestampHuman)) {
+ throw Error('Invalid real timestamp format');
+ }
+
+ // Add trailing Z if it isn't there yet
+ if (timestampHuman[timestampHuman.length - 1] !== 'Z') {
+ timestampHuman += 'Z';
+ }
+
+ // Date.parse only considers up to millisecond precision
+ let nanoSeconds = 0;
+ if (timestampHuman.includes('.')) {
+ const milliseconds = timestampHuman.split('.')[1].replace('Z', '');
+ nanoSeconds = Math.floor(Number(milliseconds.padEnd(9, '0').slice(3)));
+ }
+
+ return new RealTimestamp(
+ BigInt(Date.parse(timestampHuman)) * BigInt(TimeUtils.TO_NANO['ms']) + BigInt(nanoSeconds)
+ );
+ }
+
+ static formattedKotlinTimestamp(time: any, timestampType: TimestampType): string {
+ if (timestampType === TimestampType.ELAPSED) {
+ return TimeUtils.format(new ElapsedTimestamp(BigInt(time.elapsedNanos.toString())));
+ }
+
+ if (timestampType === TimestampType.REAL) {
+ return TimeUtils.format(new RealTimestamp(BigInt(time.unixNanos.toString())));
+ }
+
+ throw new Error('Unsupported timestamp');
+ }
+
+ static TO_NANO = {
+ ns: 1,
+ ms: 1000000,
+ s: 1000000 * 1000,
+ m: 1000000 * 1000 * 60,
+ h: 1000000 * 1000 * 60 * 60,
+ d: 1000000 * 1000 * 60 * 60 * 24,
+ };
+
+ static units = [
+ {nanosInUnit: TimeUtils.TO_NANO['ns'], unit: 'ns'},
+ {nanosInUnit: TimeUtils.TO_NANO['ms'], unit: 'ms'},
+ {nanosInUnit: TimeUtils.TO_NANO['s'], unit: 's'},
+ {nanosInUnit: TimeUtils.TO_NANO['m'], unit: 'm'},
+ {nanosInUnit: TimeUtils.TO_NANO['h'], unit: 'h'},
+ {nanosInUnit: TimeUtils.TO_NANO['d'], unit: 'd'},
+ ];
+
+ // (?=.) checks there is at least one character with a lookahead match
+ static readonly HUMAN_ELAPSED_TIMESTAMP_REGEX =
+ /^(?=.)([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?([0-9]+ns)?$/;
+ static readonly HUMAN_REAL_TIMESTAMP_REGEX =
+ /^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\.[0-9]{3}([0-9]{6})?Z?$/;
+ static readonly NS_TIMESTAMP_REGEX = /^\s*[0-9]+(\s?ns)?\s*$/;
+}
diff --git a/tools/winscope/src/common/time_utils_test.ts b/tools/winscope/src/common/time_utils_test.ts
new file mode 100644
index 0000000..b3c24b3
--- /dev/null
+++ b/tools/winscope/src/common/time_utils_test.ts
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2022 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 {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+import {TimeUtils} from './time_utils';
+
+describe('TimeUtils', () => {
+ const MILLISECOND = BigInt(1000000);
+ const SECOND = BigInt(1000) * MILLISECOND;
+ const MINUTE = BigInt(60) * SECOND;
+ const HOUR = BigInt(60) * MINUTE;
+ const DAY = BigInt(24) * HOUR;
+
+ describe('compareFn', () => {
+ it('throws if timestamps have different type', () => {
+ const real = new RealTimestamp(10n);
+ const elapsed = new ElapsedTimestamp(10n);
+
+ expect(() => {
+ TimeUtils.compareFn(real, elapsed);
+ }).toThrow();
+ });
+
+ it('allows to sort arrays', () => {
+ const array = [
+ new RealTimestamp(100n),
+ new RealTimestamp(10n),
+ new RealTimestamp(12n),
+ new RealTimestamp(110n),
+ new RealTimestamp(11n),
+ ];
+ array.sort(TimeUtils.compareFn);
+
+ const expected = [
+ new RealTimestamp(10n),
+ new RealTimestamp(11n),
+ new RealTimestamp(12n),
+ new RealTimestamp(100n),
+ new RealTimestamp(110n),
+ ];
+ expect(array).toEqual(expected);
+ });
+ });
+
+ it('nanosecondsToHuman', () => {
+ expect(TimeUtils.format(new ElapsedTimestamp(0n), true)).toEqual('0ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(0n), false)).toEqual('0ns');
+ expect(TimeUtils.format(new ElapsedTimestamp(1000n), true)).toEqual('0ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(1000n), false)).toEqual('1000ns');
+ expect(TimeUtils.format(new ElapsedTimestamp(MILLISECOND - 1n), true)).toEqual('0ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(MILLISECOND), true)).toEqual('1ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(10n * MILLISECOND), true)).toEqual('10ms');
+
+ expect(TimeUtils.format(new ElapsedTimestamp(SECOND - 1n), true)).toEqual('999ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(SECOND), true)).toEqual('1s0ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(SECOND + MILLISECOND), true)).toEqual('1s1ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(SECOND + MILLISECOND), false)).toEqual('1s1ms0ns');
+
+ expect(TimeUtils.format(new ElapsedTimestamp(MINUTE - 1n), true)).toEqual('59s999ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(MINUTE), true)).toEqual('1m0s0ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(MINUTE + SECOND + MILLISECOND), true)).toEqual(
+ '1m1s1ms'
+ );
+ expect(
+ TimeUtils.format(new ElapsedTimestamp(MINUTE + SECOND + MILLISECOND + 1n), true)
+ ).toEqual('1m1s1ms');
+ expect(
+ TimeUtils.format(new ElapsedTimestamp(MINUTE + SECOND + MILLISECOND + 1n), false)
+ ).toEqual('1m1s1ms1ns');
+
+ expect(TimeUtils.format(new ElapsedTimestamp(HOUR - 1n), true)).toEqual('59m59s999ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(HOUR - 1n), false)).toEqual('59m59s999ms999999ns');
+ expect(TimeUtils.format(new ElapsedTimestamp(HOUR), true)).toEqual('1h0m0s0ms');
+ expect(
+ TimeUtils.format(new ElapsedTimestamp(HOUR + MINUTE + SECOND + MILLISECOND), true)
+ ).toEqual('1h1m1s1ms');
+
+ expect(TimeUtils.format(new ElapsedTimestamp(DAY - 1n), true)).toEqual('23h59m59s999ms');
+ expect(TimeUtils.format(new ElapsedTimestamp(DAY), true)).toEqual('1d0h0m0s0ms');
+ expect(
+ TimeUtils.format(new ElapsedTimestamp(DAY + HOUR + MINUTE + SECOND + MILLISECOND), true)
+ ).toEqual('1d1h1m1s1ms');
+ });
+
+ it('humanElapsedToNanoseconds', () => {
+ expect(TimeUtils.parseHumanElapsed('0ns')).toEqual(new ElapsedTimestamp(0n));
+ expect(TimeUtils.parseHumanElapsed('1000ns')).toEqual(new ElapsedTimestamp(1000n));
+ expect(TimeUtils.parseHumanElapsed('0ms')).toEqual(new ElapsedTimestamp(0n));
+ expect(TimeUtils.parseHumanElapsed('1ms')).toEqual(new ElapsedTimestamp(MILLISECOND));
+ expect(TimeUtils.parseHumanElapsed('10ms')).toEqual(new ElapsedTimestamp(10n * MILLISECOND));
+
+ expect(TimeUtils.parseHumanElapsed('999ms')).toEqual(new ElapsedTimestamp(999n * MILLISECOND));
+ expect(TimeUtils.parseHumanElapsed('1s')).toEqual(new ElapsedTimestamp(SECOND));
+ expect(TimeUtils.parseHumanElapsed('1s0ms')).toEqual(new ElapsedTimestamp(SECOND));
+ expect(TimeUtils.parseHumanElapsed('1s0ms0ns')).toEqual(new ElapsedTimestamp(SECOND));
+ expect(TimeUtils.parseHumanElapsed('1s0ms1ns')).toEqual(new ElapsedTimestamp(SECOND + 1n));
+ expect(TimeUtils.parseHumanElapsed('0d1s1ms')).toEqual(
+ new ElapsedTimestamp(SECOND + MILLISECOND)
+ );
+
+ expect(TimeUtils.parseHumanElapsed('1m0s0ms')).toEqual(new ElapsedTimestamp(MINUTE));
+ expect(TimeUtils.parseHumanElapsed('1m1s1ms')).toEqual(
+ new ElapsedTimestamp(MINUTE + SECOND + MILLISECOND)
+ );
+
+ expect(TimeUtils.parseHumanElapsed('1h0m')).toEqual(new ElapsedTimestamp(HOUR));
+ expect(TimeUtils.parseHumanElapsed('1h1m1s1ms')).toEqual(
+ new ElapsedTimestamp(HOUR + MINUTE + SECOND + MILLISECOND)
+ );
+
+ expect(TimeUtils.parseHumanElapsed('1d0s1ms')).toEqual(new ElapsedTimestamp(DAY + MILLISECOND));
+ expect(TimeUtils.parseHumanElapsed('1d1h1m1s1ms')).toEqual(
+ new ElapsedTimestamp(DAY + HOUR + MINUTE + SECOND + MILLISECOND)
+ );
+
+ expect(TimeUtils.parseHumanElapsed('1d')).toEqual(new ElapsedTimestamp(DAY));
+ expect(TimeUtils.parseHumanElapsed('1d1ms')).toEqual(new ElapsedTimestamp(DAY + MILLISECOND));
+ });
+
+ it('humanToNanoseconds throws on invalid input format', () => {
+ const invalidFormatError = new Error('Invalid elapsed timestamp format');
+ expect(() => TimeUtils.parseHumanElapsed('1d1h1m1s0ns1ms')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanElapsed('1dns')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanElapsed('100')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanElapsed('')).toThrow(invalidFormatError);
+ });
+
+ it('nanosecondsToHumanReal', () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(TimeUtils.format(new RealTimestamp(0n), true)).toEqual('1970-01-01T00:00:00.000');
+ expect(
+ TimeUtils.format(
+ new RealTimestamp(
+ NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186n * MILLISECOND + 123212n
+ ),
+ true
+ )
+ ).toEqual('2022-11-10T22:04:54.186');
+ expect(TimeUtils.format(new RealTimestamp(NOV_10_2022), true)).toEqual(
+ '2022-11-10T00:00:00.000'
+ );
+ expect(TimeUtils.format(new RealTimestamp(NOV_10_2022 + 1n), true)).toEqual(
+ '2022-11-10T00:00:00.000'
+ );
+
+ expect(TimeUtils.format(new RealTimestamp(0n), false)).toEqual('1970-01-01T00:00:00.000000000');
+ expect(
+ TimeUtils.format(
+ new RealTimestamp(
+ NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186n * MILLISECOND + 123212n
+ ),
+ false
+ )
+ ).toEqual('2022-11-10T22:04:54.186123212');
+ expect(TimeUtils.format(new RealTimestamp(NOV_10_2022), false)).toEqual(
+ '2022-11-10T00:00:00.000000000'
+ );
+ expect(TimeUtils.format(new RealTimestamp(NOV_10_2022 + 1n), false)).toEqual(
+ '2022-11-10T00:00:00.000000001'
+ );
+ });
+
+ it('humanRealToNanoseconds', () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(TimeUtils.parseHumanReal('2022-11-10T22:04:54.186123212')).toEqual(
+ new RealTimestamp(
+ NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186n * MILLISECOND + 123212n
+ )
+ );
+ expect(TimeUtils.parseHumanReal('2022-11-10T22:04:54.186123212Z')).toEqual(
+ new RealTimestamp(1668117894186123212n)
+ );
+ expect(TimeUtils.parseHumanReal('2022-11-10T22:04:54.186000212')).toEqual(
+ new RealTimestamp(1668117894186000212n)
+ );
+ expect(TimeUtils.parseHumanReal('2022-11-10T22:04:54.006000002')).toEqual(
+ new RealTimestamp(1668117894006000002n)
+ );
+ expect(TimeUtils.parseHumanReal('2022-11-10T06:04:54.006000002')).toEqual(
+ new RealTimestamp(1668060294006000002n)
+ );
+ });
+
+ it('canReverseDateFormatting', () => {
+ let timestamp = new RealTimestamp(1668117894186123212n);
+ expect(TimeUtils.parseHumanReal(TimeUtils.format(timestamp))).toEqual(timestamp);
+
+ timestamp = new ElapsedTimestamp(DAY + HOUR + MINUTE + SECOND + MILLISECOND + 1n);
+ expect(TimeUtils.parseHumanElapsed(TimeUtils.format(timestamp))).toEqual(timestamp);
+ });
+
+ it('humanToNanoseconds throws on invalid input format', () => {
+ const invalidFormatError = new Error('Invalid real timestamp format');
+ expect(() => TimeUtils.parseHumanReal('23h59m59s999ms5ns')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanReal('1d')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanReal('100')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanReal('06h4m54s, 10 Nov 2022')).toThrow(invalidFormatError);
+ expect(() => TimeUtils.parseHumanReal('')).toThrow(invalidFormatError);
+ });
+
+ it('nano second regex accept all expected inputs', () => {
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test('123')).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test('123ns')).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test('123 ns')).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test(' 123 ns ')).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test(' 123 ')).toBeTrue();
+
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test('1a23')).toBeFalse();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test('a123 ns')).toBeFalse();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test('')).toBeFalse();
+ });
+
+ it('format real', () => {
+ expect(TimeUtils.format(Timestamp.from(TimestampType.REAL, 100n, 500n))).toEqual(
+ '1970-01-01T00:00:00.000000600'
+ );
+ expect(
+ TimeUtils.format(Timestamp.from(TimestampType.REAL, 100n * MILLISECOND, 500n), true)
+ ).toEqual('1970-01-01T00:00:00.100');
+ });
+
+ it('format elapsed', () => {
+ expect(
+ TimeUtils.format(Timestamp.from(TimestampType.ELAPSED, 100n * MILLISECOND, 500n), true)
+ ).toEqual('100ms');
+ expect(
+ TimeUtils.format(Timestamp.from(TimestampType.ELAPSED, 100n * MILLISECOND), true)
+ ).toEqual('100ms');
+ expect(
+ TimeUtils.format(Timestamp.from(TimestampType.ELAPSED, 100n * MILLISECOND, 500n))
+ ).toEqual('100ms0ns');
+ expect(TimeUtils.format(Timestamp.from(TimestampType.ELAPSED, 100n * MILLISECOND))).toEqual(
+ '100ms0ns'
+ );
+ });
+});
diff --git a/tools/winscope/src/common/tree_utils.ts b/tools/winscope/src/common/tree_utils.ts
new file mode 100644
index 0000000..f94dad9
--- /dev/null
+++ b/tools/winscope/src/common/tree_utils.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface TreeNode {
+ name: string;
+ parent?: TreeNode;
+ children?: TreeNode[];
+}
+
+type FilterType = (node: TreeNode | undefined | null) => boolean;
+
+class TreeUtils {
+ static findDescendantNode(node: TreeNode, isTargetNode: FilterType): TreeNode | undefined {
+ if (isTargetNode(node)) {
+ return node;
+ }
+
+ if (!node.children) {
+ return;
+ }
+
+ for (const child of node.children) {
+ const target = TreeUtils.findDescendantNode(child, isTargetNode);
+ if (target) {
+ return target;
+ }
+ }
+
+ return undefined;
+ }
+
+ static findAncestorNode(node: TreeNode, isTargetNode: FilterType): TreeNode | undefined {
+ let ancestor = node.parent;
+
+ while (ancestor && !isTargetNode(ancestor)) {
+ ancestor = ancestor.parent;
+ }
+
+ return ancestor;
+ }
+
+ static makeNodeFilter(filterString: string): FilterType {
+ const filter = (item: TreeNode | undefined | null) => {
+ if (item) {
+ const regex = new RegExp(filterString, 'i');
+ return filterString.length === 0 || regex.test(`${item.name}`);
+ }
+ return false;
+ };
+ return filter;
+ }
+}
+
+export {TreeNode, TreeUtils, FilterType};
diff --git a/tools/winscope/src/components/FlatCard.vue b/tools/winscope/src/components/FlatCard.vue
deleted file mode 100644
index 38c3356..0000000
--- a/tools/winscope/src/components/FlatCard.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<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
deleted file mode 100644
index 8c428b8..0000000
--- a/tools/winscope/src/components/IconSelection/IconSelectOption.vue
+++ /dev/null
@@ -1,164 +0,0 @@
-<!-- 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/components/TagDisplay/Arrow.vue b/tools/winscope/src/components/TagDisplay/Arrow.vue
deleted file mode 100644
index 0a19705..0000000
--- a/tools/winscope/src/components/TagDisplay/Arrow.vue
+++ /dev/null
@@ -1,33 +0,0 @@
-<!-- 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="arrow"/>
-</template>
-<script>
-export default {
- name: 'arrow',
-};
-</script>
-<style scoped>
-.arrow {
- display: inline-block;
- width: 0;
- height: 0;
- border-left: 6px solid transparent;
- border-right: 6px solid transparent;
- border-top: 10px solid;
-}
-</style>
\ No newline at end of file
diff --git a/tools/winscope/src/components/TagDisplay/TransitionContainer.vue b/tools/winscope/src/components/TagDisplay/TransitionContainer.vue
deleted file mode 100644
index 13a1cde..0000000
--- a/tools/winscope/src/components/TagDisplay/TransitionContainer.vue
+++ /dev/null
@@ -1,113 +0,0 @@
-<!-- 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="transition-container" :style="transitionStyle" @click="handleTransitionClick()">
- <md-tooltip md-direction="left"> {{tooltip}} </md-tooltip>
- <arrow class="arrow-start" :style="transitionComponentColor"/>
- <div class="connector" :style="transitionComponentColor"/>
- <arrow class="arrow-end" :style="transitionComponentColor"/>
- </div>
-</template>
-<script>
-import Arrow from './Arrow.vue';
-import LocalStore from '../../localstore.js';
-
-var transitionCount = false;
-
-export default {
- name: 'transition-container',
- components: {
- 'arrow': Arrow,
- },
- props: {
- 'width': {
- type: Number,
- },
- 'startPos': {
- type: Number,
- },
- 'startTime': {
- type: Number,
- },
- 'endTime': {
- type: Number,
- },
- 'color': {
- type: String,
- },
- 'overlap': {
- type: Number,
- },
- 'tooltip': {
- type: String,
- },
- 'store': {
- type: LocalStore,
- },
- },
- methods: {
- handleTransitionClick() {
- if (transitionCount) {
- this.$store.dispatch('updateTimelineTime', this.startTime);
- transitionCount = false;
- } else {
- this.$store.dispatch('updateTimelineTime', this.endTime);
- transitionCount = true;
- }
- },
- },
- computed: {
- transitionStyle() {
- return {
- width: this.width + '%',
- left: this.startPos + '%',
- bottom: this.overlap * 100 + '%',
- }
- },
- transitionComponentColor() {
- return {
- borderTopColor: this.color,
- }
- },
- },
-};
-</script>
-<style scoped>
-.transition-container {
- position: absolute;
- height: 15px;
- display: inline-flex;
-}
-
-.arrow-start {
- position: absolute;
- left: 0%;
-}
-
-.arrow-end {
- position: absolute;
- right: 0%;
-}
-
-.connector {
- position: absolute;
- display: inline-block;
- width: auto;
- height: 9px;
- left: 5px;
- right: 5px;
- border-top: 1px solid;
-}
-</style>
\ No newline at end of file
diff --git a/tools/winscope/src/cross_tool/cross_tool_protocol.ts b/tools/winscope/src/cross_tool/cross_tool_protocol.ts
new file mode 100644
index 0000000..b69d236
--- /dev/null
+++ b/tools/winscope/src/cross_tool/cross_tool_protocol.ts
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils} from 'common/function_utils';
+import {OnBugreportReceived, RemoteBugreportReceiver} from 'interfaces/remote_bugreport_receiver';
+import {OnTimestampReceived, RemoteTimestampReceiver} from 'interfaces/remote_timestamp_receiver';
+import {RemoteTimestampSender} from 'interfaces/remote_timestamp_sender';
+import {RealTimestamp} from 'trace/timestamp';
+import {Message, MessageBugReport, MessagePong, MessageTimestamp, MessageType} from './messages';
+import {OriginAllowList} from './origin_allow_list';
+
+class RemoteTool {
+ constructor(readonly window: Window, readonly origin: string) {}
+}
+
+export class CrossToolProtocol
+ implements RemoteBugreportReceiver, RemoteTimestampReceiver, RemoteTimestampSender
+{
+ private remoteTool?: RemoteTool;
+ private onBugreportReceived: OnBugreportReceived = FunctionUtils.DO_NOTHING_ASYNC;
+ private onTimestampReceived: OnTimestampReceived = FunctionUtils.DO_NOTHING;
+
+ constructor() {
+ window.addEventListener('message', async (event) => {
+ await this.onMessageReceived(event);
+ });
+ }
+
+ setOnBugreportReceived(callback: OnBugreportReceived) {
+ this.onBugreportReceived = callback;
+ }
+
+ setOnTimestampReceived(callback: OnTimestampReceived) {
+ this.onTimestampReceived = callback;
+ }
+
+ sendTimestamp(timestamp: RealTimestamp) {
+ if (!this.remoteTool) {
+ return;
+ }
+
+ const message = new MessageTimestamp(timestamp.getValueNs());
+ this.remoteTool.window.postMessage(message, this.remoteTool.origin);
+ console.log('Cross-tool protocol sent timestamp message:', message);
+ }
+
+ private async onMessageReceived(event: MessageEvent) {
+ if (!OriginAllowList.isAllowed(event.origin)) {
+ console.log(
+ 'Cross-tool protocol ignoring message from non-allowed origin.',
+ 'Origin:',
+ event.origin,
+ 'Message:',
+ event.data
+ );
+ return;
+ }
+
+ const message = event.data as Message;
+ if (message.type === undefined) {
+ return;
+ }
+
+ if (!this.remoteTool) {
+ this.remoteTool = new RemoteTool(event.source as Window, event.origin);
+ }
+
+ switch (message.type) {
+ case MessageType.PING:
+ console.log('Cross-tool protocol received ping message:', message);
+ (event.source as Window).postMessage(new MessagePong(), event.origin);
+ break;
+ case MessageType.PONG:
+ console.log('Cross-tool protocol received unexpected pong message:', message);
+ break;
+ case MessageType.BUGREPORT:
+ console.log('Cross-tool protocol received bugreport message:', message);
+ await this.onMessageBugreportReceived(message as MessageBugReport);
+ break;
+ case MessageType.TIMESTAMP:
+ console.log('Cross-tool protocol received timestamp message:', message);
+ this.onMessageTimestampReceived(message as MessageTimestamp);
+ break;
+ case MessageType.FILES:
+ console.log('Cross-tool protocol received unexpected files message', message);
+ break;
+ default:
+ console.log('Cross-tool protocol received unsupported message type:', message);
+ break;
+ }
+ }
+
+ private async onMessageBugreportReceived(message: MessageBugReport) {
+ const timestamp =
+ message.timestampNs !== undefined ? new RealTimestamp(message.timestampNs) : undefined;
+ this.onBugreportReceived(message.file, timestamp);
+ }
+
+ private onMessageTimestampReceived(message: MessageTimestamp) {
+ const timestamp = new RealTimestamp(message.timestampNs);
+ this.onTimestampReceived(timestamp);
+ }
+}
diff --git a/tools/winscope/src/cross_tool/cross_tool_protocol_stub.ts b/tools/winscope/src/cross_tool/cross_tool_protocol_stub.ts
new file mode 100644
index 0000000..a1adab7
--- /dev/null
+++ b/tools/winscope/src/cross_tool/cross_tool_protocol_stub.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils} from 'common/function_utils';
+import {OnBugreportReceived, RemoteBugreportReceiver} from 'interfaces/remote_bugreport_receiver';
+import {OnTimestampReceived, RemoteTimestampReceiver} from 'interfaces/remote_timestamp_receiver';
+import {RemoteTimestampSender} from 'interfaces/remote_timestamp_sender';
+import {RealTimestamp} from 'trace/timestamp';
+
+export class CrossToolProtocolStub
+ implements RemoteBugreportReceiver, RemoteTimestampReceiver, RemoteTimestampSender
+{
+ onBugreportReceived: OnBugreportReceived = FunctionUtils.DO_NOTHING_ASYNC;
+ onTimestampReceived: OnTimestampReceived = FunctionUtils.DO_NOTHING;
+
+ setOnBugreportReceived(callback: OnBugreportReceived) {
+ this.onBugreportReceived = callback;
+ }
+
+ setOnTimestampReceived(callback: OnTimestampReceived) {
+ this.onTimestampReceived = callback;
+ }
+
+ sendTimestamp(timestamp: RealTimestamp) {
+ // do nothing
+ }
+}
diff --git a/tools/winscope/src/cross_tool/messages.ts b/tools/winscope/src/cross_tool/messages.ts
new file mode 100644
index 0000000..3e1e4bd
--- /dev/null
+++ b/tools/winscope/src/cross_tool/messages.ts
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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 enum MessageType {
+ UNKNOWN = 0,
+ PING,
+ PONG,
+ BUGREPORT,
+ TIMESTAMP,
+ FILES,
+}
+
+export interface Message {
+ type: MessageType;
+}
+
+export class MessagePing implements Message {
+ type = MessageType.PING;
+}
+
+export class MessagePong implements Message {
+ type = MessageType.PONG;
+}
+
+export class MessageBugReport implements Message {
+ type = MessageType.BUGREPORT;
+
+ constructor(public file: File, public timestampNs?: bigint, public issueId?: string) {}
+}
+
+export class MessageTimestamp implements Message {
+ type = MessageType.TIMESTAMP;
+
+ constructor(public timestampNs: bigint, public sections?: string[]) {}
+}
+
+export class MessageFiles implements Message {
+ type = MessageType.FILES;
+
+ constructor(public files: File[], public timestampNs?: bigint, public issueId?: string) {}
+}
diff --git a/tools/winscope/src/cross_tool/origin_allow_list.ts b/tools/winscope/src/cross_tool/origin_allow_list.ts
new file mode 100644
index 0000000..55199c4
--- /dev/null
+++ b/tools/winscope/src/cross_tool/origin_allow_list.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 {globalConfig} from 'common/global_config';
+
+export class OriginAllowList {
+ private static readonly ALLOW_LIST_PROD = [
+ new RegExp('^https://([^\\/]*\\.)*googleplex\\.com$'),
+ new RegExp('^https://([^\\/]*\\.)*google\\.com$'),
+ ];
+
+ private static readonly ALLOW_LIST_DEV = [
+ ...OriginAllowList.ALLOW_LIST_PROD,
+ new RegExp('^(http|https)://localhost:8081$'), // remote tool mock
+ ];
+
+ static isAllowed(originUrl: string, mode = globalConfig.MODE): boolean {
+ const list = OriginAllowList.getList(mode);
+
+ for (const regex of list) {
+ if (regex.test(originUrl)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static getList(mode: typeof globalConfig.MODE): RegExp[] {
+ switch (mode) {
+ case 'DEV':
+ return OriginAllowList.ALLOW_LIST_DEV;
+ case 'PROD':
+ return OriginAllowList.ALLOW_LIST_PROD;
+ default:
+ throw new Error(`Unhandled mode: ${globalConfig.MODE}`);
+ }
+ }
+}
diff --git a/tools/winscope/src/cross_tool/origin_allow_list_test.ts b/tools/winscope/src/cross_tool/origin_allow_list_test.ts
new file mode 100644
index 0000000..88d02eb
--- /dev/null
+++ b/tools/winscope/src/cross_tool/origin_allow_list_test.ts
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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 {OriginAllowList} from './origin_allow_list';
+
+describe('OriginAllowList', () => {
+ describe('dev mode', () => {
+ const mode = 'DEV' as const;
+
+ it('allows localhost', () => {
+ expect(OriginAllowList.isAllowed('http://localhost:8081', mode)).toBeTrue();
+ expect(OriginAllowList.isAllowed('https://localhost:8081', mode)).toBeTrue();
+ });
+ });
+
+ describe('prod mode', () => {
+ const mode = 'PROD' as const;
+
+ it('allows google.com', () => {
+ expect(OriginAllowList.isAllowed('https://google.com', mode)).toBeTrue();
+ expect(OriginAllowList.isAllowed('https://subdomain.google.com', mode)).toBeTrue();
+ });
+
+ it('denies pseudo google.com', () => {
+ expect(OriginAllowList.isAllowed('https://evilgoogle.com', mode)).toBeFalse();
+ expect(OriginAllowList.isAllowed('https://evil.com/google.com', mode)).toBeFalse();
+ });
+
+ it('allows googleplex.com', () => {
+ expect(OriginAllowList.isAllowed('https://googleplex.com', mode)).toBeTrue();
+ expect(OriginAllowList.isAllowed('https://subdomain.googleplex.com', mode)).toBeTrue();
+ });
+
+ it('denies pseudo googleplex.com', () => {
+ expect(OriginAllowList.isAllowed('https://evilgoogleplex.com', mode)).toBeFalse();
+ expect(
+ OriginAllowList.isAllowed('https://evil.com/subdomain.googleplex.com', mode)
+ ).toBeFalse();
+ });
+ });
+});
diff --git a/tools/winscope/src/decode.js b/tools/winscope/src/decode.js
deleted file mode 100644
index 4d9b511..0000000
--- a/tools/winscope/src/decode.js
+++ /dev/null
@@ -1,748 +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.
- */
-
-/* eslint-disable camelcase */
-/* eslint-disable max-len */
-
-import jsonProtoDefsAccessibility from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto';
-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/services/surfaceflinger/layerproto/transactions.proto';
-import jsonProtoDefsTransactionLegacy 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 jsonProtoDefsTags from 'platform_testing/libraries/flicker/src/com/android/server/wm/proto/tags.proto';
-import jsonProtoDefsErrors from 'platform_testing/libraries/flicker/src/com/android/server/wm/proto/errors.proto';
-import protobuf from 'protobufjs';
-import {transform_accessibility_trace} from './transform_accessibility.js';
-import {transform_transaction_trace} from './transform_transaction.js';
-import {transform_transaction_trace_legacy} from './transform_transaction_legacy.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 {mp4Decoder} from './decodeVideo.js';
-
-import AccessibilityTrace from '@/traces/Accessibility.ts';
-import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
-import WindowManagerTrace from '@/traces/WindowManager.ts';
-import TransactionsTrace from '@/traces/Transactions.ts';
-import TransactionsTraceLegacy from '@/traces/TransactionsLegacy.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';
-
-import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
-import WindowManagerDump from '@/dumps/WindowManager.ts';
-import WaylandDump from '@/dumps/Wayland.ts';
-
-import TagTrace from '@/traces/TraceTag.ts';
-import ErrorTrace from '@/traces/TraceError.ts';
-
-const AccessibilityTraceMessage = lookup_type(jsonProtoDefsAccessibility, 'com.android.server.accessibility.AccessibilityTraceFileProto');
-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, 'TransactionTraceFile');
-const SfTransactionTraceMessageLegacy = lookup_type(jsonProtoDefsTransactionLegacy, '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 TagTraceMessage = lookup_type(jsonProtoDefsTags, 'com.android.server.wm.flicker.FlickerTagTraceProto');
-const ErrorTraceMessage = lookup_type(jsonProtoDefsErrors, 'com.android.server.wm.flicker.FlickerErrorTraceProto');
-
-const ACCESSIBILITY_MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
-const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
-const TRANSACTIONS_TRACE_MAGIC_NUMBER = [0x09, 0x54, 0x4e, 0x58, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TNXTRACE
-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 TAG_TRACE_MAGIC_NUMBER = [0x09, 0x54, 0x41, 0x47, 0x54, 0x52, 0x41, 0x43, 0x45]; //.TAGTRACE
-const ERROR_TRACE_MAGIC_NUMBER = [0x09, 0x45, 0x52, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; //.ERRORTRACE
-
-const FILE_TYPES = Object.freeze({
- ACCESSIBILITY_TRACE: 'AccessibilityTrace',
- WINDOW_MANAGER_TRACE: 'WindowManagerTrace',
- SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace',
- WINDOW_MANAGER_DUMP: 'WindowManagerDump',
- SURFACE_FLINGER_DUMP: 'SurfaceFlingerDump',
- SCREEN_RECORDING: 'ScreenRecording',
- TRANSACTIONS_TRACE: 'TransactionsTrace',
- TRANSACTIONS_TRACE_LEGACY: 'TransactionsTraceLegacy',
- 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',
- TAG_TRACE: 'TagTrace',
- ERROR_TRACE: 'ErrorTrace',
-});
-
-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 ACCESSIBILITY_ICON = 'filter_none';
-const TAG_ICON = 'details';
-const TRACE_ERROR_ICON = 'warning';
-
-const FILE_ICONS = {
- [FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
- [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.TRANSACTIONS_TRACE_LEGACY]: 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,
- [FILE_TYPES.TAG_TRACE]: TAG_ICON,
- [FILE_TYPES.ERROR_TRACE]: TRACE_ERROR_ICON,
-};
-
-function oneOf(dataType) {
- return {oneOf: true, type: dataType};
-}
-
-const TRACE_TYPES = Object.freeze({
- ACCESSIBILITY: 'AccessibilityTrace',
- WINDOW_MANAGER: 'WindowManagerTrace',
- SURFACE_FLINGER: 'SurfaceFlingerTrace',
- SCREEN_RECORDING: 'ScreenRecording',
- TRANSACTION: 'Transaction',
- TRANSACTION_LEGACY: 'Transaction (Legacy)',
- WAYLAND: 'Wayland',
- PROTO_LOG: 'ProtoLog',
- SYSTEM_UI: 'SystemUI',
- LAUNCHER: 'Launcher',
- IME_CLIENTS: 'ImeTrace Clients',
- IME_SERVICE: 'ImeTrace InputMethodService',
- IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
- TAG: 'TagTrace',
- ERROR: 'ErrorTrace',
-});
-
-const TRACE_INFO = {
- [TRACE_TYPES.ACCESSIBILITY]: {
- name: 'Accessibility',
- icon: ACCESSIBILITY_ICON,
- files: [oneOf(FILE_TYPES.ACCESSIBILITY_TRACE)],
- constructor: AccessibilityTrace,
- },
- [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.TRANSACTION_LEGACY]: {
- name: 'Transactions (Legacy)',
- icon: TRANSACTION_ICON,
- files: [
- oneOf(FILE_TYPES.TRANSACTIONS_TRACE_LEGACY),
- ],
- constructor: TransactionsTraceLegacy,
- },
- [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,
- },
- [TRACE_TYPES.TAG]: {
- name: 'Tag',
- icon: TAG_ICON,
- files: [oneOf(FILE_TYPES.TAG_TRACE)],
- constructor: TagTrace,
- },
- [TRACE_TYPES.ERROR]: {
- name: 'Error',
- icon: TRACE_ERROR_ICON,
- files: [oneOf(FILE_TYPES.ERROR_TRACE)],
- constructor: ErrorTrace,
- },
-};
-
-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.TRANSACTION_LEGACY]: 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,
- [TRACE_TYPES.TAG_TRACE]: TAG_ICON,
- [TRACE_TYPES.ERROR_TRACE]: TRACE_ERROR_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.ACCESSIBILITY_TRACE]: {
- name: 'Accessibility trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.ACCESSIBILITY_TRACE,
- objTypeProto: AccessibilityTraceMessage,
- transform: transform_accessibility_trace,
- timeline: true,
- },
- },
- [FILE_TYPES.WINDOW_MANAGER_TRACE]: {
- name: 'WindowManager trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.WINDOW_MANAGER_TRACE,
- objTypeProto: WmTraceMessage,
- transform: WindowManagerTrace.fromProto,
- timeline: true,
- },
- },
- [FILE_TYPES.SURFACE_FLINGER_TRACE]: {
- name: 'SurfaceFlinger trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.SURFACE_FLINGER_TRACE,
- mime: 'application/octet-stream',
- objTypeProto: SfTraceMessage,
- transform: SurfaceFlingerTrace.fromProto,
- timeline: true,
- },
- },
- [FILE_TYPES.WAYLAND_TRACE]: {
- name: 'Wayland trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.WAYLAND_TRACE,
- mime: 'application/octet-stream',
- objTypeProto: WaylandTraceMessage,
- transform: transform_wayland_trace,
- timeline: true,
- },
- },
- [FILE_TYPES.SURFACE_FLINGER_DUMP]: {
- name: 'SurfaceFlinger dump',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.SURFACE_FLINGER_DUMP,
- mime: 'application/octet-stream',
- objTypeProto: [SfDumpMessage, SfTraceMessage],
- transform: [SurfaceFlingerDump.fromProto, SurfaceFlingerTrace.fromProto],
- timeline: true,
- },
- },
- [FILE_TYPES.WINDOW_MANAGER_DUMP]: {
- name: 'WindowManager dump',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.WINDOW_MANAGER_DUMP,
- mime: 'application/octet-stream',
- objTypeProto: WmDumpMessage,
- transform: WindowManagerDump.fromProto,
- timeline: true,
- },
- },
- [FILE_TYPES.WAYLAND_DUMP]: {
- name: 'Wayland dump',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.WAYLAND_DUMP,
- mime: 'application/octet-stream',
- objTypeProto: WaylandDumpMessage,
- transform: transform_wl_outputstate,
- timeline: true,
- },
- },
- [FILE_TYPES.SCREEN_RECORDING]: {
- name: 'Screen recording',
- decoder: videoDecoder,
- decoderParams: {
- type: FILE_TYPES.SCREEN_RECORDING,
- mime: 'video/mp4',
- videoDecoder: mp4Decoder,
- },
- },
- [FILE_TYPES.TRANSACTIONS_TRACE]: {
- name: 'Transaction',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.TRANSACTIONS_TRACE,
- mime: 'application/octet-stream',
- objTypeProto: SfTransactionTraceMessage,
- transform: transform_transaction_trace,
- timeline: true,
- },
- },
- [FILE_TYPES.TRANSACTIONS_TRACE_LEGACY]: {
- name: 'Transactions (Legacy)',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.TRANSACTIONS_TRACE_LEGACY,
- mime: 'application/octet-stream',
- objTypeProto: SfTransactionTraceMessageLegacy,
- transform: transform_transaction_trace_legacy,
- timeline: true,
- },
- },
- [FILE_TYPES.PROTO_LOG]: {
- name: 'ProtoLog',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.PROTO_LOG,
- mime: 'application/octet-stream',
- objTypeProto: ProtoLogMessage,
- transform: transformProtolog,
- timeline: true,
- },
- },
- [FILE_TYPES.SYSTEM_UI]: {
- name: 'SystemUI trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.SYSTEM_UI,
- mime: 'application/octet-stream',
- objTypeProto: SystemUiTraceMessage,
- transform: transform_sysui_trace,
- timeline: true,
- },
- },
- [FILE_TYPES.LAUNCHER]: {
- name: 'Launcher trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.LAUNCHER,
- mime: 'application/octet-stream',
- objTypeProto: 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',
- objTypeProto: 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',
- objTypeProto: 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',
- objTypeProto: InputMethodManagerServiceTraceMessage,
- transform: transform_ime_trace_managerservice,
- timeline: true,
- },
- },
- [FILE_TYPES.TAG_TRACE]: {
- name: 'Tag trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.TAG_TRACE,
- objTypeProto: TagTraceMessage,
- transform: TagTrace.fromProto,
- timeline: true,
- },
- },
- [FILE_TYPES.ERROR_TRACE]: {
- name: 'Error trace',
- decoder: protoDecoder,
- decoderParams: {
- type: FILE_TYPES.ERROR_TRACE,
- objTypeProto: ErrorTraceMessage,
- transform: ErrorTrace.fromProto,
- timeline: true,
- },
- },
-};
-
-function lookup_type(protoPath, type) {
- return protobuf.Root.fromJSON(protoPath).lookupType(type);
-}
-
-// Replace enum values with string representation and
-// add default values to the proto objects. This function also handles
-// a special case with TransformProtos where the matrix may be derived
-// from the transform type.
-function modifyProtoFields(protoObj, displayDefaults) {
- if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) {
- return;
- }
-
- for (const fieldName in protoObj.$type.fields) {
- if (protoObj.$type.fields.hasOwnProperty(fieldName)) {
- const fieldProperties = protoObj.$type.fields[fieldName];
- const field = protoObj[fieldName];
-
- if (Array.isArray(field)) {
- field.forEach((item, _) => {
- modifyProtoFields(item, displayDefaults);
- });
- continue;
- }
-
- if (displayDefaults && !(field)) {
- protoObj[fieldName] = fieldProperties.defaultValue;
- }
-
- if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
- protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
- continue;
- }
- modifyProtoFields(protoObj[fieldName], displayDefaults);
- }
- }
-}
-
-function decodeAndTransformProto(buffer, params, displayDefaults) {
- var objTypesProto = [];
- var transforms = [];
- if (!Array.isArray(params.objTypeProto)) {
- objTypesProto = [params.objTypeProto];
- transforms = [params.transform];
- } else {
- objTypesProto = params.objTypeProto;
- transforms = params.transform;
- }
- // each trace or dump may have different processors, for example, until S, SF dumps
- // returne a list of layers and winscope built a [LayerTraceEntry] from them.
- // From S onwards, returns a LayerTrace object, iterating over multiple items allows
- // winscope to handle both the new and legacy formats
- // TODO Refactor the decode.js code into a set of decoders to clean up the code
- let lastError = null;
- for (var x = 0; x < objTypesProto.length; x++) {
- const objType = objTypesProto[x];
- const transform = transforms[x];
- try {
- const decoded = objType.decode(buffer);
- modifyProtoFields(decoded, displayDefaults);
- const transformed = transform(decoded);
- return transformed;
- } catch (e) {
- lastError = e;
- // check next parser
- }
- }
-
- if (lastError) {
- throw lastError;
- }
- throw new UndetectableFileType('Unable to parse file');
-}
-
-function protoDecoder(buffer, params, fileName, store) {
- const transformed = decodeAndTransformProto(buffer, params, store.displayDefaults);
-
- // add tagGenerationTrace to dataFile for WM/SF traces so tags can be generated
- var tagGenerationTrace = null;
- if (params.type === FILE_TYPES.WINDOW_MANAGER_TRACE ||
- params.type === FILE_TYPES.SURFACE_FLINGER_TRACE) {
- tagGenerationTrace = transformed;
- }
-
- let data;
- if (params.timeline) {
- data = transformed.entries ?? transformed.children;
- } else {
- data = [transformed];
- }
- const blobUrl = URL.createObjectURL(new Blob([buffer], {type: params.mime}));
-
- return dataFile(
- fileName,
- data.map((x) => x.timestamp),
- data,
- blobUrl,
- params.type,
- tagGenerationTrace
- );
-}
-
-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, tagGenerationTrace = null) {
- return {
- filename: filename,
- // 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,
- tagGenerationTrace: tagGenerationTrace,
- type: type,
- selectedIndex: 0,
- destroy() {
- URL.revokeObjectURL(this.blobUrl);
- },
- };
-}
-
-function arrayEquals(a, b) {
- if (a.length !== b.length) {
- return false;
- }
- for (let i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- return false;
- }
- }
- return true;
-}
-
-function arrayStartsWith(array, prefix) {
- return arrayEquals(array.slice(0, prefix.length), prefix);
-}
-
-function decodedFile(fileType, buffer, 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.SURFACE_FLINGER_TRACE, buffer, fileName, store);
- }
- if (arrayStartsWith(buffer, ACCESSIBILITY_MAGIC_NUMBER)) {
- return decodedFile(FILE_TYPES.ACCESSIBILITY_TRACE, buffer, fileName, store);
- }
- if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
- 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);
- }
- if (arrayStartsWith(buffer, TRANSACTIONS_TRACE_MAGIC_NUMBER)) {
- return decodedFile(FILE_TYPES.TRANSACTIONS_TRACE, buffer, fileName, store);
- }
- if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) {
- 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);
- }
- if (arrayStartsWith(buffer, SYSTEM_UI_MAGIC_NUMBER)) {
- return decodedFile(FILE_TYPES.SYSTEM_UI, buffer, fileName, store);
- }
- if (arrayStartsWith(buffer, LAUNCHER_MAGIC_NUMBER)) {
- return decodedFile(FILE_TYPES.LAUNCHER, buffer, fileName, store);
- }
- 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);
- }
- if (arrayStartsWith(buffer, TAG_TRACE_MAGIC_NUMBER)) {
- return decodedFile(FILE_TYPES.TAG_TRACE, buffer, fileName, store);
- }
- if (arrayStartsWith(buffer, ERROR_TRACE_MAGIC_NUMBER)) {
- return decodedFile(FILE_TYPES.ERROR_TRACE, 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_LEGACY, (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 {
- 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 fall through to next filetype
- }
- }
- throw new UndetectableFileType('Unable to detect file');
-}
-
-/**
- * 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 {
- dataFile,
- detectAndDecode,
- decodeAndTransformProto,
- TagTraceMessage,
- FILE_TYPES,
- TRACE_INFO,
- TRACE_TYPES,
- DUMP_TYPES,
- DUMP_INFO,
- FILE_DECODERS,
- FILE_ICONS,
- UndetectableFileType
-};
diff --git a/tools/winscope/src/decodeVideo.js b/tools/winscope/src/decodeVideo.js
deleted file mode 100644
index 63bef90..0000000
--- a/tools/winscope/src/decodeVideo.js
+++ /dev/null
@@ -1,69 +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.
- */
-
-const WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!#
-
-// Suitable only for short patterns
-function findFirstInArray(array, pattern) {
- for (var i = 0; i < array.length; i++) {
- var match = true;
- for (var j = 0; j < pattern.length; j++) {
- if (array[i + j] != pattern[j]) {
- match = false;
- break;
- }
- }
- if (match) {
- return i;
- }
- }
- return -1;
-}
-
-function parseUintNLE(buffer, position, bytes) {
- var num = 0;
- for (var i = bytes - 1; i >= 0; i--) {
- num = num * 256
- num += buffer[position + i];
- }
- return num;
-}
-
-function parseUint32LE(buffer, position) {
- return parseUintNLE(buffer, position, 4)
-}
-
-function parseUint64LE(buffer, position) {
- return parseUintNLE(buffer, position, 8)
-}
-
-function mp4Decoder(buffer) {
- var dataStart = findFirstInArray(buffer, WINSCOPE_META_MAGIC_STRING);
- if (dataStart < 0) {
- throw new Error('Unable to find sync metadata in the file. Are you using the latest Android ScreenRecorder version?');
- }
- dataStart += WINSCOPE_META_MAGIC_STRING.length;
- var frameNum = parseUint32LE(buffer, dataStart);
- dataStart += 4;
- var timeline = [];
- for (var i = 0; i < frameNum; i++) {
- timeline.push(parseUint64LE(buffer, dataStart) * 1000);
- dataStart += 8;
- }
- return [buffer, timeline]
-}
-
-export { mp4Decoder };
diff --git a/tools/winscope/src/dumps/DumpBase.ts b/tools/winscope/src/dumps/DumpBase.ts
deleted file mode 100644
index 38c86c4..0000000
--- a/tools/winscope/src/dumps/DumpBase.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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(): readonly any[] {
- return Object.values(this._files).flat();
- }
-
- abstract get type(): String;
-}
-
-interface IDump {
- files: readonly 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
deleted file mode 100644
index c3b6a88..0000000
--- a/tools/winscope/src/dumps/SurfaceFlinger.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 LayersTraceEntry from '../flickerlib/layers/LayerTraceEntry';
-import LayersTrace from '../flickerlib/LayersTrace';
-
-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;
- }
-
- static fromProto(proto: any): LayersTrace {
- const source = null;
- const entry = LayersTraceEntry.fromProto(
- /* protos */ proto.layers,
- /* displays */ proto.displays,
- /* timestamp */ 0,
- /* hwcBlob */ ""
- );
- return new LayersTrace([entry], source);
- }
-}
\ No newline at end of file
diff --git a/tools/winscope/src/dumps/Wayland.ts b/tools/winscope/src/dumps/Wayland.ts
deleted file mode 100644
index b038d97..0000000
--- a/tools/winscope/src/dumps/Wayland.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 21b78ac..0000000
--- a/tools/winscope/src/dumps/WindowManager.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 { WindowManagerTrace } 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: any): WindowManagerTrace {
- const source = null;
- const state = WindowManagerTrace.fromDump(proto);
- return new WindowManagerTrace([state], source);
- }
-}
\ No newline at end of file
diff --git a/tools/winscope/static/favicon.svg b/tools/winscope/src/favicon.svg
similarity index 100%
rename from tools/winscope/static/favicon.svg
rename to tools/winscope/src/favicon.svg
diff --git a/tools/winscope/src/flickerlib/ErrorTrace.ts b/tools/winscope/src/flickerlib/ErrorTrace.ts
deleted file mode 100644
index 36b8307..0000000
--- a/tools/winscope/src/flickerlib/ErrorTrace.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 { ErrorTrace } from "./common"
-import ErrorState from "./errors/ErrorState"
-
-ErrorTrace.fromProto = function (proto: any) {
- const states = [];
- for (const stateProto of proto.states) {
- const transformedState = ErrorState.fromProto(
- stateProto.errors,
- stateProto.timestamp);
-
- states.push(transformedState);
- }
- const source = null;
- return new ErrorTrace(states, source);
-}
-
-export default ErrorTrace;
diff --git a/tools/winscope/src/flickerlib/LayersTrace.ts b/tools/winscope/src/flickerlib/LayersTrace.ts
deleted file mode 100644
index 0172011..0000000
--- a/tools/winscope/src/flickerlib/LayersTrace.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 { LayersTrace } from "./common"
-import LayerTraceEntryLazy from './layers/LayerTraceEntryLazy'
-
-LayersTrace.fromProto = function (proto: any): LayersTrace {
- const entries = []
- for (const entryProto of proto.entry) {
- const transformedEntry = new LayerTraceEntryLazy(
- /* protos */ entryProto.layers.layers,
- /* displays */ entryProto.displays,
- /* timestamp */ entryProto.elapsedRealtimeNanos,
- /* hwcBlob */ entryProto.hwcBlob);
-
- entries.push(transformedEntry);
- }
- const source = null;
- const trace = new LayersTrace(entries, source);
- return trace;
-}
-
-export default LayersTrace;
diff --git a/tools/winscope/src/flickerlib/ObjectFormatter.ts b/tools/winscope/src/flickerlib/ObjectFormatter.ts
deleted file mode 100644
index c9eac1c..0000000
--- a/tools/winscope/src/flickerlib/ObjectFormatter.ts
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright 2021, 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 {toSize, toActiveBuffer, toColor, toColor3, toPoint, toRect,
- toRectF, toRegion, toMatrix22, toTransform} from './common';
-import intDefMapping from
- '../../../../../prebuilts/misc/common/winscope/intDefMapping.json';
-import config from '../config/Configuration.json'
-
-function readIntdefMap(): Map<string, string> {
- const map = new Map<string, string>();
- const keys = Object.keys(config.intDefColumn);
-
- keys.forEach(key => {
- const value = config.intDefColumn[key];
- map.set(key, value);
- });
-
- return map;
-}
-export default class ObjectFormatter {
- static displayDefaults: boolean = false
- private static INVALID_ELEMENT_PROPERTIES = config.invalidProperties;
-
- private static FLICKER_INTDEF_MAP = readIntdefMap();
-
- static cloneObject(entry: any): any {
- let obj: any = {}
- const properties = ObjectFormatter.getProperties(entry);
- properties.forEach(prop => obj[prop] = entry[prop]);
- return obj;
- }
-
- /**
- * Get the true properties of an entry excluding functions, kotlin gernerated
- * variables, explicitly excluded properties, and flicker objects already in
- * the hierarchy that shouldn't be traversed when formatting the entry
- * @param entry The entry for which we want to get the properties for
- * @return The "true" properties of the entry as described above
- */
- static getProperties(entry: any): string[] {
- var props = [];
- let obj = entry;
-
- do {
- const properties = Object.getOwnPropertyNames(obj).filter(it => {
- // filter out functions
- if (typeof(entry[it]) === 'function') return false;
- // internal propertires from kotlinJs
- if (it.includes(`$`)) return false;
- // private kotlin variables from kotlin
- if (it.startsWith(`_`)) return false;
- // some predefined properties used only internally (e.g., children, ref, diff)
- if (this.INVALID_ELEMENT_PROPERTIES.includes(it)) return false;
-
- const value = entry[it];
- // only non-empty arrays of non-flicker objects (otherwise they are in hierarchy)
- if (Array.isArray(value) && value.length > 0) return !value[0].stableId;
- // non-flicker object
- return !(value?.stableId);
- });
- properties.forEach(function (prop) {
- if (typeof(entry[prop]) !== 'function' && props.indexOf(prop) === -1) {
- props.push(prop);
- }
- });
- } while (obj = Object.getPrototypeOf(obj));
-
- return props;
- }
-
- /**
- * Format a Winscope entry to be displayed in the UI
- * Accounts for different user display settings (e.g. hiding empty/default values)
- * @param obj The raw object to format
- * @return The formatted object
- */
- static format(obj: any): {} {
- const properties = this.getProperties(obj);
- const sortedProperties = properties.sort()
-
- const result: any = {}
- sortedProperties.forEach(entry => {
- const key = entry;
- const value: any = obj[key];
-
- if (value === null || value === undefined) {
- if (this.displayDefaults) {
- result[key] = value;
- }
- return
- }
-
- if (value || this.displayDefaults) {
- // raw values (e.g., false or 0)
- if (!value) {
- result[key] = value
- // flicker obj
- } else if (value.prettyPrint) {
- const isEmpty = value.isEmpty === true;
- if (!isEmpty || this.displayDefaults) {
- result[key] = value.prettyPrint();
- }
- } else {
- // converted proto to flicker
- const translatedObject = this.translateObject(value);
- if (translatedObject) {
- if (translatedObject.prettyPrint) {
- result[key] = translatedObject.prettyPrint();
- }
- else {
- result[key] = translatedObject;
- }
- // objects - recursive call
- } else if (value && typeof(value) == `object`) {
- const childObj = this.format(value) as any;
- const isEmpty = Object.entries(childObj).length == 0 || childObj.isEmpty;
- if (!isEmpty || this.displayDefaults) {
- result[key] = childObj;
- }
- } else {
- // values
- result[key] = this.translateIntDef(obj, key, value);
- }
- }
-
- }
- })
-
- return result;
- }
-
- /**
- * Translate some predetermined proto objects into their flicker equivalent
- *
- * Returns null if the object cannot be translated
- *
- * @param obj Object to translate
- */
- private static translateObject(obj) {
- const type = obj?.$type?.name;
- switch(type) {
- case `SizeProto`: return toSize(obj);
- case `ActiveBufferProto`: return toActiveBuffer(obj);
- case `Color3`: return toColor3(obj);
- case `ColorProto`: return toColor(obj);
- case `PointProto`: return toPoint(obj);
- case `RectProto`: return toRect(obj);
- case `Matrix22`: return toMatrix22(obj);
- case `FloatRectProto`: return toRectF(obj);
- case `RegionProto`: return toRegion(obj);
- case `TransformProto`: return toTransform(obj);
- case 'ColorTransformProto': {
- const formatted = this.formatColorTransform(obj.val);
- return `${formatted}`;
- }
- }
-
- return null;
- }
-
- private static formatColorTransform(vals) {
- 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;
- }
-
- /**
- * Obtains from the proto field, the metadata related to the typedef type (if any)
- *
- * @param obj Proto object
- * @param propertyName Property to search
- */
- private static getTypeDefSpec(obj: any, propertyName: string): string {
- const fields = obj?.$type?.fields;
- if (!fields) {
- return null;
- }
-
- const options = fields[propertyName]?.options;
- if (!options) {
- return null;
- }
-
- return options["(.android.typedef)"];
- }
-
- /**
- * Translate intdef properties into their string representation
- *
- * For proto objects check the
- *
- * @param parentObj Object containing the value to parse
- * @param propertyName Property to search
- * @param value Property value
- */
- private static translateIntDef(parentObj: any, propertyName: string, value: any): string {
- const parentClassName = parentObj.constructor.name;
- const propertyPath = `${parentClassName}.${propertyName}`;
-
- let translatedValue = value;
- // Parse Flicker objects (no intdef annotation supported)
- if (this.FLICKER_INTDEF_MAP.has(propertyPath)) {
- translatedValue = this.getIntFlagsAsStrings(value,
- this.FLICKER_INTDEF_MAP.get(propertyPath));
- } else {
- // If it's a proto, search on the proto definition for the intdef type
- const typeDefSpec = this.getTypeDefSpec(parentObj, propertyName);
- if (typeDefSpec) {
- translatedValue = this.getIntFlagsAsStrings(value, typeDefSpec);
- }
- }
-
- return translatedValue;
- }
-
- /**
- * Translate a property from its numerical value into its string representation
- *
- * @param intFlags Property value
- * @param annotationType IntDef type to use
- */
- private static getIntFlagsAsStrings(intFlags: any, annotationType: string) {
- const flags = [];
-
- const mapping = intDefMapping[annotationType].values;
- const knownFlagValues = Object.keys(mapping).reverse().map(x => parseInt(x));
-
- if (mapping.length == 0) {
- console.warn("No mapping for type", annotationType)
- return intFlags + ""
- }
-
- // Will only contain bits that have not been associated with a flag.
- const parsedIntFlags = parseInt(intFlags);
- let leftOver = parsedIntFlags;
-
- for (const flagValue of knownFlagValues) {
- if (((leftOver & flagValue) && ((intFlags & flagValue) === flagValue))
- || (parsedIntFlags === 0 && flagValue === 0)) {
- flags.push(mapping[flagValue]);
-
- leftOver = leftOver & ~flagValue;
- }
- }
-
- 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(' | ');
- }
-}
diff --git a/tools/winscope/src/flickerlib/TagTrace.ts b/tools/winscope/src/flickerlib/TagTrace.ts
deleted file mode 100644
index 862e51f..0000000
--- a/tools/winscope/src/flickerlib/TagTrace.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 { TagTrace } from "./common"
-import TagState from "./tags/TagState"
-
-TagTrace.fromProto = function (proto: any): TagTrace {
- const states = [];
- for (const stateProto of proto.states) {
- const transformedState = TagState.fromProto(
- stateProto.timestamp,
- stateProto.tags);
-
- states.push(transformedState);
- }
- const source = null;
- return new TagTrace(states, source);
-}
-
-export default TagTrace;
diff --git a/tools/winscope/src/flickerlib/WindowManagerState.ts b/tools/winscope/src/flickerlib/WindowManagerState.ts
deleted file mode 100644
index 8bed56f..0000000
--- a/tools/winscope/src/flickerlib/WindowManagerState.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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 {
- KeyguardControllerState,
- RootWindowContainer,
- WindowManagerPolicy,
- WindowManagerState
-} from "./common"
-
-import WindowContainer from "./windows/WindowContainer"
-
-WindowManagerState.fromProto = function (proto: any, timestamp: number = 0, where: string = ""): WindowManagerState {
- var inputMethodWIndowAppToken = "";
- if (proto.inputMethodWindow != null) {
- proto.inputMethodWindow.hashCode.toString(16)
- };
-
- const rootWindowContainer = createRootWindowContainer(proto.rootWindowContainer);
- const keyguardControllerState = createKeyguardControllerState(
- proto.rootWindowContainer.keyguardController);
- const policy = createWindowManagerPolicy(proto.policy);
-
- const entry = new WindowManagerState(
- where,
- policy,
- proto.focusedApp,
- proto.focusedDisplayId,
- proto.focusedWindow?.title ?? "",
- inputMethodWIndowAppToken,
- proto.rootWindowContainer.isHomeRecentsComponent,
- proto.displayFrozen,
- proto.rootWindowContainer.pendingActivities.map(it => it.title),
- rootWindowContainer,
- keyguardControllerState,
- /*timestamp */ `${timestamp}`
- );
-
- addAttributes(entry, proto);
- return entry
-}
-
-function addAttributes(entry: WindowManagerState, proto: any) {
- entry.kind = entry.constructor.name;
- // There no JVM/JS translation for Longs yet
- entry.timestampMs = entry.timestamp.toString();
- entry.rects = entry.windowStates.reverse().map(it => it.rect);
- if (!entry.isComplete()) {
- entry.isIncompleteReason = entry.getIsIncompleteReason();
- }
- entry.proto = proto;
- entry.shortName = entry.name;
- entry.chips = [];
- entry.isVisible = true;
-}
-
-function createWindowManagerPolicy(proto: any): WindowManagerPolicy {
- return new WindowManagerPolicy(
- proto.focusedAppToken ?? "",
- proto.forceStatusBar,
- proto.forceStatusBarFromKeyguard,
- proto.keyguardDrawComplete,
- proto.keyguardOccluded,
- proto.keyguardOccludedChanged,
- proto.keyguardOccludedPending,
- proto.lastSystemUiFlags,
- proto.orientation,
- proto.rotation,
- proto.rotationMode,
- proto.screenOnFully,
- proto.windowManagerDrawComplete
- );
-}
-
-function createRootWindowContainer(proto: any): RootWindowContainer {
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.windowContainer,
- /* childrenProto */ proto.windowContainer?.children?.reverse() ?? [],
- /* isActivityInTree */ false
- );
-
- if (windowContainer == null) {
- throw new Error(`Window container should not be null.\n${JSON.stringify(proto)}`);
- }
- const entry = new RootWindowContainer(windowContainer);
- return entry;
-}
-
-function createKeyguardControllerState(proto: any): KeyguardControllerState {
- const keyguardOccludedStates = {};
-
- if (proto) {
- proto.keyguardOccludedStates.forEach(it =>
- keyguardOccludedStates[it.displayId] = it.keyguardOccluded);
- }
-
- return new KeyguardControllerState(
- proto?.isAodShowing ?? false,
- proto?.isKeyguardShowing ?? false,
- keyguardOccludedStates
- );
-}
-
-export default WindowManagerState;
diff --git a/tools/winscope/src/flickerlib/WindowManagerTrace.ts b/tools/winscope/src/flickerlib/WindowManagerTrace.ts
deleted file mode 100644
index c30c620..0000000
--- a/tools/winscope/src/flickerlib/WindowManagerTrace.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 WindowManagerState from "./WindowManagerState"
-
-WindowManagerTrace.fromProto = function (proto: any) {
- const entries = [];
- for (const entryProto of proto.entry) {
- const transformedEntry = WindowManagerState.fromProto(
- entryProto.windowManagerService,
- entryProto.elapsedRealtimeNanos,
- entryProto.where);
-
- entries.push(transformedEntry);
- }
- const source = null;
- return new WindowManagerTrace(entries, source);
-}
-
-WindowManagerTrace.fromDump = function(proto: any): WindowManagerTrace {
- return WindowManagerState.fromProto(proto);
-}
-
-export default WindowManagerTrace;
diff --git a/tools/winscope/src/flickerlib/common.js b/tools/winscope/src/flickerlib/common.js
deleted file mode 100644
index cca16f4..0000000
--- a/tools/winscope/src/flickerlib/common.js
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * 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
-
-// WM
-const WindowManagerTrace = require('flicker').com.android.server.wm.traces.
- common.windowmanager.WindowManagerTrace;
-const WindowManagerState = require('flicker').com.android.server.wm.traces.
- common.windowmanager.WindowManagerState;
-const Activity = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.Activity;
-const Configuration = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.Configuration;
-const ConfigurationContainer = require('flicker').com.android.server.wm.traces.
- common.windowmanager.windows.ConfigurationContainer;
-const DisplayArea = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.DisplayArea;
-const DisplayContent = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.DisplayContent;
-const KeyguardControllerState = require('flicker').com.android.server.wm.
- traces.common.windowmanager.windows.KeyguardControllerState;
-const RootWindowContainer = require('flicker').com.android.server.wm.traces.
- common.windowmanager.windows.RootWindowContainer;
-const Task = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.Task;
-const TaskFragment = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.TaskFragment;
-const WindowConfiguration = require('flicker').com.android.server.wm.traces.
- common.windowmanager.windows.WindowConfiguration;
-const WindowContainer = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.WindowContainer;
-const WindowLayoutParams= require('flicker').com.android.server.wm.traces.
- common.windowmanager.windows.WindowLayoutParams;
-const WindowManagerPolicy = require('flicker').com.android.server.wm.traces.
- common.windowmanager.windows.WindowManagerPolicy;
-const WindowState = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.WindowState;
-const WindowToken = require('flicker').com.android.server.wm.traces.common.
- windowmanager.windows.WindowToken;
-
-// SF
-const Layer = require('flicker').com.android.server.wm.traces.common.
- layers.Layer;
-const BaseLayerTraceEntry = require('flicker').com.android.server.wm.traces.common.
- layers.BaseLayerTraceEntry;
-const LayerTraceEntry = require('flicker').com.android.server.wm.traces.common.
- layers.LayerTraceEntry;
-const LayerTraceEntryBuilder = require('flicker').com.android.server.wm.traces.
- common.layers.LayerTraceEntryBuilder;
-const LayersTrace = require('flicker').com.android.server.wm.traces.common.
- layers.LayersTrace;
-const Matrix22 = require('flicker').com.android.server.wm.traces.common
- .Matrix22;
-const Matrix33 = require('flicker').com.android.server.wm.traces.common
- .Matrix33;
-const Transform = require('flicker').com.android.server.wm.traces.common.
- layers.Transform;
-const Display = require('flicker').com.android.server.wm.traces.common.
- layers.Display;
-
-// Common
-const Size = require('flicker').com.android.server.wm.traces.common.Size;
-const ActiveBuffer = require('flicker').com.android.server.wm.traces.common
- .ActiveBuffer;
-const Color3 = require('flicker').com.android.server.wm.traces.common.Color3;
-const Color = require('flicker').com.android.server.wm.traces.common.Color;
-const Point = require('flicker').com.android.server.wm.traces.common.Point;
-const Rect = require('flicker').com.android.server.wm.traces.common.Rect;
-const RectF = require('flicker').com.android.server.wm.traces.common.RectF;
-const Region = require('flicker').com.android.server.wm.traces.common.region.Region;
-
-//Tags
-const Tag = require('flicker').com.android.server.wm.traces.common.tags.Tag;
-const TagState = require('flicker').com.android.server.wm.traces.common.tags.TagState;
-const TagTrace = require('flicker').com.android.server.wm.traces.common.tags.TagTrace;
-
-//Errors
-const Error = require('flicker').com.android.server.wm.traces.common.errors.Error;
-const ErrorState = require('flicker').com.android.server.wm.traces.common.errors.ErrorState;
-const ErrorTrace = require('flicker').com.android.server.wm.traces.common.errors.ErrorTrace;
-
-// Service
-const TaggingEngine = require('flicker').com.android.server.wm.traces.common.service.TaggingEngine;
-
-const EMPTY_BUFFER = new ActiveBuffer(0, 0, 0, 0);
-const EMPTY_COLOR3 = new Color3(-1, -1, -1);
-const EMPTY_COLOR = new Color(-1, -1, -1, 0);
-const EMPTY_RECT = new Rect(0, 0, 0, 0);
-const EMPTY_RECTF = new RectF(0, 0, 0, 0);
-const EMPTY_POINT = new Point(0, 0);
-const EMPTY_MATRIX22 = new Matrix22(0, 0, 0, 0, 0, 0);
-const EMPTY_MATRIX33 = new Matrix33(0, 0, 0, 0, 0, 0);
-const EMPTY_TRANSFORM = new Transform(0, EMPTY_MATRIX33);
-
-function toSize(proto) {
- if (proto == null) {
- return EMPTY_BOUNDS;
- }
- const width = proto.width ?? proto.w ?? 0;
- const height = proto.height ?? proto.h ?? 0;
- if (width || height) {
- return new Size(width, height);
- }
- return EMPTY_BOUNDS;
-}
-
-function toActiveBuffer(proto) {
- const width = proto?.width ?? 0;
- const height = proto?.height ?? 0;
- const stride = proto?.stride ?? 0;
- const format = proto?.format ?? 0;
-
- if (width || height || stride || format) {
- return new ActiveBuffer(width, height, stride, format);
- }
- return EMPTY_BUFFER;
-}
-
-function toColor3(proto) {
- if (proto == null) {
- return EMPTY_COLOR;
- }
- const r = proto.r ?? 0;
- const g = proto.g ?? 0;
- const b = proto.b ?? 0;
- if (r || g || b) {
- return new Color3(r, g, b);
- }
- return EMPTY_COLOR3;
-}
-
-function toColor(proto) {
- if (proto == null) {
- return EMPTY_COLOR;
- }
- const r = proto.r ?? 0;
- const g = proto.g ?? 0;
- const b = proto.b ?? 0;
- const a = proto.a ?? 0;
- if (r || g || b || a) {
- return new Color(r, g, b, a);
- }
- return EMPTY_COLOR;
-}
-
-function toPoint(proto) {
- if (proto == null) {
- return null;
- }
- const x = proto.x ?? 0;
- const y = proto.y ?? 0;
- if (x || y) {
- return new Point(x, y);
- }
- return EMPTY_POINT;
-}
-
-function toRect(proto) {
- if (proto == null) {
- return EMPTY_RECT;
- }
-
- const left = proto?.left ?? 0;
- const top = proto?.top ?? 0;
- const right = proto?.right ?? 0;
- const bottom = proto?.bottom ?? 0;
- if (left || top || right || bottom) {
- return new Rect(left, top, right, bottom);
- }
- return EMPTY_RECT;
-}
-
-function toRectF(proto) {
- if (proto == null) {
- return EMPTY_RECTF;
- }
-
- const left = proto?.left ?? 0;
- const top = proto?.top ?? 0;
- const right = proto?.right ?? 0;
- const bottom = proto?.bottom ?? 0;
- if (left || top || right || bottom) {
- return new RectF(left, top, right, bottom);
- }
- return EMPTY_RECTF;
-}
-
-function toRegion(proto) {
- if (proto == null) {
- return null;
- }
-
- const rects = [];
- for (let x = 0; x < proto.rect.length; x++) {
- const rect = proto.rect[x];
- const parsedRect = toRect(rect);
- rects.push(parsedRect);
- }
-
- return new Region(rects);
-}
-
-function toTransform(proto) {
- if (proto == null) {
- return EMPTY_TRANSFORM;
- }
- const dsdx = proto.dsdx ?? 0;
- const dtdx = proto.dtdx ?? 0;
- const tx = proto.tx ?? 0;
- const dsdy = proto.dsdy ?? 0;
- const dtdy = proto.dtdy ?? 0;
- const ty = proto.ty ?? 0;
-
- if (dsdx || dtdx || tx || dsdy || dtdy || ty) {
- const matrix = new Matrix33(dsdx, dtdx, tx, dsdy, dtdy, ty);
- return new Transform(proto.type ?? 0, matrix);
- }
-
- if (proto.type) {
- return new Transform(proto.type ?? 0, EMPTY_MATRIX33);
- }
- return EMPTY_TRANSFORM;
-}
-
-function toMatrix22(proto) {
- if (proto == null) {
- return EMPTY_MATRIX22;
- }
- const dsdx = proto.dsdx ?? 0;
- const dtdx = proto.dtdx ?? 0;
- const dsdy = proto.dsdy ?? 0;
- const dtdy = proto.dtdy ?? 0;
-
- if (dsdx || dtdx || dsdy || dtdy) {
- return new Matrix22(dsdx, dtdx, dsdy, dtdy);
- }
-
- return EMPTY_MATRIX22;
-}
-
-export {
- Activity,
- Configuration,
- ConfigurationContainer,
- DisplayArea,
- DisplayContent,
- KeyguardControllerState,
- RootWindowContainer,
- Task,
- TaskFragment,
- WindowConfiguration,
- WindowContainer,
- WindowState,
- WindowToken,
- WindowLayoutParams,
- WindowManagerPolicy,
- WindowManagerTrace,
- WindowManagerState,
- // SF
- BaseLayerTraceEntry,
- Layer,
- LayerTraceEntry,
- LayerTraceEntryBuilder,
- LayersTrace,
- Transform,
- Matrix22,
- Matrix33,
- Display,
- // Tags
- Tag,
- TagState,
- TagTrace,
- // Errors
- Error,
- ErrorState,
- ErrorTrace,
- // Common
- Size,
- ActiveBuffer,
- Color,
- Color3,
- Point,
- Rect,
- RectF,
- Region,
- // Service
- TaggingEngine,
- toSize,
- toActiveBuffer,
- toColor,
- toColor3,
- toPoint,
- toRect,
- toRectF,
- toRegion,
- toMatrix22,
- toTransform,
-};
diff --git a/tools/winscope/src/flickerlib/errors/Error.ts b/tools/winscope/src/flickerlib/errors/Error.ts
deleted file mode 100644
index eb81715..0000000
--- a/tools/winscope/src/flickerlib/errors/Error.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2021, 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 { Error } from "../common"
-
-Error.fromProto = function (proto: any): Error {
- const error = new Error(
- proto.stacktrace,
- proto.message,
- proto.layerId,
- proto.windowToken,
- proto.taskId,
- proto.assertionName
- );
- return error;
-}
-
-export default Error;
diff --git a/tools/winscope/src/flickerlib/errors/ErrorState.ts b/tools/winscope/src/flickerlib/errors/ErrorState.ts
deleted file mode 100644
index 533af23..0000000
--- a/tools/winscope/src/flickerlib/errors/ErrorState.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 { ErrorState } from "../common";
-import Error from './Error';
-
-ErrorState.fromProto = function (protos: any[], timestamp: number): ErrorState {
- const errors = protos.map(it => Error.fromProto(it));
- const state = new ErrorState(errors, `${timestamp}`);
- return state;
-}
-
-export default ErrorState;
diff --git a/tools/winscope/src/flickerlib/index.js b/tools/winscope/src/flickerlib/index.js
deleted file mode 100644
index 216d621..0000000
--- a/tools/winscope/src/flickerlib/index.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 LayersTrace from './LayersTrace';
-import WindowManagerState from './WindowManagerState';
-import WindowManagerTrace from './WindowManagerTrace';
-import ObjectFormatter from './ObjectFormatter';
-import TagTrace from './TagTrace';
-import ErrorTrace from './ErrorTrace';
-/**
- * Entry point into the flickerlib for Winscope.
- * Expose everything we want Winscope to have access to here.
- */
-export {ObjectFormatter, LayersTrace, WindowManagerState, WindowManagerTrace, TagTrace, ErrorTrace};
-
diff --git a/tools/winscope/src/flickerlib/layers/Layer.ts b/tools/winscope/src/flickerlib/layers/Layer.ts
deleted file mode 100644
index efcf648..0000000
--- a/tools/winscope/src/flickerlib/layers/Layer.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2021, 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 { Layer, Rect, toActiveBuffer, toColor, toRect, toRectF, toRegion } from "../common"
-import { shortenName } from '../mixin'
-import { RELATIVE_Z_CHIP, GPU_CHIP, HWC_CHIP } from '../treeview/Chips'
-import Transform from './Transform'
-
-Layer.fromProto = function (proto: any): Layer {
- const visibleRegion = toRegion(proto.visibleRegion)
- const activeBuffer = toActiveBuffer(proto.activeBuffer)
- const bounds = toRectF(proto.bounds)
- const color = toColor(proto.color)
- const screenBounds = toRectF(proto.screenBounds)
- const sourceBounds = toRectF(proto.sourceBounds)
- const transform = Transform.fromProto(proto.transform, proto.position)
- const bufferTransform = Transform.fromProto(proto.bufferTransform, /* position */ null)
- const hwcCrop = toRectF(proto.hwcCrop)
- const hwcFrame = toRect(proto.hwcFrame)
- let crop: Rect
- if (proto.crop) {
- crop = toRect(proto.crop)
- };
-
- const entry = new Layer(
- proto.name ?? ``,
- proto.id,
- proto.parent,
- proto.z,
- visibleRegion,
- activeBuffer,
- proto.flags,
- bounds,
- color,
- proto.isOpaque,
- proto.shadowRadius,
- proto.cornerRadius,
- proto.type ?? ``,
- screenBounds,
- transform,
- sourceBounds,
- proto.currFrame,
- proto.effectiveScalingMode,
- bufferTransform,
- proto.hwcCompositionType,
- hwcCrop,
- hwcFrame,
- proto.backgroundBlurRadius,
- crop,
- proto.isRelativeOf,
- proto.zOrderRelativeOf,
- proto.layerStack
- );
-
- addAttributes(entry, proto);
- return entry
-}
-
-function addAttributes(entry: Layer, proto: any) {
- entry.kind = `${entry.id}`;
- entry.shortName = shortenName(entry.name);
- entry.proto = proto;
- entry.rect = entry.bounds;
- entry.rect.transform = entry.transform;
- entry.rect.ref = entry;
- entry.rect.label = entry.name;
- entry.chips = [];
- updateChips(entry);
-}
-
-function updateChips(entry) {
- if ((entry.zOrderRelativeOf || -1) !== -1) {
- entry.chips.push(RELATIVE_Z_CHIP);
- }
- if (entry.hwcCompositionType === 'CLIENT') {
- entry.chips.push(GPU_CHIP);
- } else if (entry.hwcCompositionType === 'DEVICE' || entry.hwcCompositionType === 'SOLID_COLOR') {
- entry.chips.push(HWC_CHIP);
- }
-}
-
-export default Layer;
diff --git a/tools/winscope/src/flickerlib/layers/LayerTraceEntry.ts b/tools/winscope/src/flickerlib/layers/LayerTraceEntry.ts
deleted file mode 100644
index 8213c2d..0000000
--- a/tools/winscope/src/flickerlib/layers/LayerTraceEntry.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2021, 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 { Display, LayerTraceEntry, LayerTraceEntryBuilder, toRect, toSize, toTransform } from "../common"
-import Layer from './Layer'
-import { VISIBLE_CHIP, RELATIVE_Z_PARENT_CHIP, MISSING_LAYER } from '../treeview/Chips'
-
-LayerTraceEntry.fromProto = function (protos: any[], displayProtos: any[],
- timestamp: number, hwcBlob: string, where: string = ''): LayerTraceEntry {
- const layers = protos.map(it => Layer.fromProto(it));
- const displays = (displayProtos || []).map(it => newDisplay(it));
- const builder = new LayerTraceEntryBuilder(timestamp, layers, displays, hwcBlob, where);
- const entry: LayerTraceEntry = builder.build();
-
- updateChildren(entry);
- addAttributes(entry, protos);
- return entry;
-}
-
-function addAttributes(entry: LayerTraceEntry, protos: any) {
- entry.kind = "entry"
- // There no JVM/JS translation for Longs yet
- entry.timestampMs = entry.timestamp.toString()
- entry.rects = entry.visibleLayers
- .sort((a, b) => (b.absoluteZ > a.absoluteZ) ? 1 : (a.absoluteZ == b.absoluteZ) ? 0 : -1)
- .map(it => it.rect);
-
- // Avoid parsing the entry root because it is an array of layers
- // containing all trace information, this slows down the property tree.
- // Instead parse only key properties for debugging
- const entryIds = {}
- protos.forEach(it =>
- entryIds[it.id] = `\nparent=${it.parent}\ntype=${it.type}\nname=${it.name}`
- );
- entry.proto = entryIds;
- entry.shortName = entry.name;
- entry.chips = [];
- entry.isVisible = true;
-}
-
-function updateChildren(entry: LayerTraceEntry) {
- entry.flattenedLayers.forEach(it => {
- if (it.isVisible) {
- it.chips.push(VISIBLE_CHIP);
- }
- if (it.zOrderRelativeOf) {
- it.chips.push(RELATIVE_Z_PARENT_CHIP);
- }
- if (it.isMissing) {
- it.chips.push(MISSING_LAYER);
- }
- });
-}
-
-function newDisplay(proto: any): Display {
- return new Display(
- proto.id,
- proto.name,
- proto.layerStack,
- toSize(proto.size),
- toRect(proto.layerStackSpaceRect),
- toTransform(proto.transform),
- proto.isVirtual
- )
-}
-
-export default LayerTraceEntry;
diff --git a/tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts b/tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts
deleted file mode 100644
index 393e97c..0000000
--- a/tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2021, 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 { BaseLayerTraceEntry } from "../common";
-import LayerTraceEntry from "./LayerTraceEntry";
-
-class LayerTraceEntryLazy extends BaseLayerTraceEntry {
- private _isInitialized: boolean = false;
- private _layersProto: any[];
- private _displayProtos: any[];
- timestamp: number;
- timestampMs: string;
- hwcBlob: string;
- where: string;
- private _lazyLayerTraceEntry: LayerTraceEntry;
-
- constructor (layersProto: any[], displayProtos: any[],
- timestamp: number, hwcBlob: string, where: string = '') {
- super();
- this._layersProto = layersProto;
- this._displayProtos = displayProtos;
- this.timestamp = timestamp;
- this.timestampMs = timestamp.toString();
- this.hwcBlob = hwcBlob;
- this.where = where;
-
- this.declareLazyProperties();
- }
-
- private initialize() {
- if (this._isInitialized) return;
-
- this._isInitialized = true;
- this._lazyLayerTraceEntry = LayerTraceEntry.fromProto(
- this._layersProto, this._displayProtos, this.timestamp,
- this.hwcBlob, this.where);
- this._layersProto = [];
- this._displayProtos = [];
- }
-
-
- private declareLazyProperties() {
- Object.defineProperty(this, 'kind', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.kind;
- }});
-
- Object.defineProperty(this, 'timestampMs', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.timestampMs;
- }});
-
- Object.defineProperty(this, 'rects', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.rects;
- }});
-
- Object.defineProperty(this, 'proto', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.proto;
- }});
-
- Object.defineProperty(this, 'shortName', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.shortName;
- }});
-
- Object.defineProperty(this, 'isVisible', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.isVisible;
- }});
-
- Object.defineProperty(this, 'flattenedLayers', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.flattenedLayers;
- }});
-
- Object.defineProperty(this, 'stableId', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.stableId;
- }});
-
- Object.defineProperty(this, 'visibleLayers', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.visibleLayers;
- }});
-
- Object.defineProperty(this, 'children', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.children;
- }});
-
- Object.defineProperty(this, 'displays', {configurable: true, enumerable: true, get: function () {
- this.initialize();
- return this._lazyLayerTraceEntry.displays;
- }});
- }
-}
-
-export default LayerTraceEntryLazy;
diff --git a/tools/winscope/src/flickerlib/layers/Transform.ts b/tools/winscope/src/flickerlib/layers/Transform.ts
deleted file mode 100644
index e0c6def..0000000
--- a/tools/winscope/src/flickerlib/layers/Transform.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2021, 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, Matrix33 } from "../common"
-
-Transform.fromProto = function (transformProto, positionProto): Transform {
- const entry = new Transform(
- transformProto?.type ?? 0,
- getMatrix(transformProto, positionProto))
-
- return entry
-}
-
-function getMatrix(transform, position): Matrix33 {
- const x = position?.x ?? 0
- const y = position?.y ?? 0
-
- if (transform == null || isSimpleTransform(transform.type)) {
- return getDefaultTransform(transform?.type, x, y)
- }
-
- return new Matrix33(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
-}
-
-function getDefaultTransform(type, x, y): Matrix33 {
- // IDENTITY
- if (!type) {
- return new Matrix33(1, 0, x, 0, 1, y)
- }
-
- // ROT_270 = ROT_90|FLIP_H|FLIP_V
- if (isFlagSet(type, ROT_90_VAL | FLIP_V_VAL | FLIP_H_VAL)) {
- return new Matrix33(0, -1, x, 1, 0, y)
- }
-
- // ROT_180 = FLIP_H|FLIP_V
- if (isFlagSet(type, FLIP_V_VAL | FLIP_H_VAL)) {
- return new Matrix33(-1, 0, x, 0, -1, y)
- }
-
- // ROT_90
- if (isFlagSet(type, ROT_90_VAL)) {
- return new Matrix33(0, 1, x, -1, 0, y)
- }
-
- // IDENTITY
- if (isFlagClear(type, SCALE_VAL | ROTATE_VAL)) {
- return new Matrix33(1, 0, x, 0, 1, y)
- }
-
- throw new Error(`Unknown transform type ${type}`)
-}
-
-export function isFlagSet(type, bits): Boolean {
- var type = type || 0;
- return (type & bits) === bits;
-}
-
-export function isFlagClear(type, bits): Boolean {
- return (type & bits) === 0;
-}
-
-export function isSimpleTransform(type): Boolean {
- return isFlagClear(type, ROT_INVALID_VAL | SCALE_VAL)
-}
-
-/* transform type flags */
-const ROTATE_VAL = 0x0002
-const SCALE_VAL = 0x0004
-
-/* orientation flags */
-const FLIP_H_VAL = 0x0100 // (1 << 0 << 8)
-const FLIP_V_VAL = 0x0200 // (1 << 1 << 8)
-const ROT_90_VAL = 0x0400 // (1 << 2 << 8)
-const ROT_INVALID_VAL = 0x8000 // (0x80 << 8)
-
-export default Transform
diff --git a/tools/winscope/src/flickerlib/mixin.ts b/tools/winscope/src/flickerlib/mixin.ts
deleted file mode 100644
index 5625ba1..0000000
--- a/tools/winscope/src/flickerlib/mixin.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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 ObjectFormatter from "./ObjectFormatter"
-
-/**
- * Get the properties of a WM object for display.
- *
- * @param entry WM hierarchy element
- * @param proto Associated proto object
- */
-export function getPropertiesForDisplay(entry: any): any {
- if (!entry) {
- return
- }
-
- let obj: any = {}
- const properties = ObjectFormatter.getProperties(entry)
- properties.forEach(prop => obj[prop] = entry[prop]);
-
- // 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.
- if (obj.children) delete obj.children
- if (obj.proto) delete obj.proto
-
- obj.proto = Object.assign({}, entry.proto)
- if (obj.proto.children) delete obj.proto.children
- if (obj.proto.childWindows) delete obj.proto.childWindows
- if (obj.proto.childrenWindows) delete obj.proto.childrenWindows
- if (obj.proto.childContainers) delete obj.proto.childContainers
- if (obj.proto.windowToken) delete obj.proto.windowToken
- if (obj.proto.rootDisplayArea) delete obj.proto.rootDisplayArea
- if (obj.proto.rootWindowContainer) delete obj.proto.rootWindowContainer
- if (obj.proto.windowContainer?.children) delete obj.proto.windowContainer.children
- obj = ObjectFormatter.format(obj)
-
- return obj
-}
-
-export function shortenName(name: any): 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}`
-}
diff --git a/tools/winscope/src/flickerlib/tags/Tag.ts b/tools/winscope/src/flickerlib/tags/Tag.ts
deleted file mode 100644
index 25a50a3..0000000
--- a/tools/winscope/src/flickerlib/tags/Tag.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2021, 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 { Tag } from "../common";
-import TransitionType from "./TransitionType";
-
-const transitionTypeMap = new Map([
- ['ROTATION', TransitionType.ROTATION],
- ['PIP_ENTER', TransitionType.PIP_ENTER],
- ['PIP_RESIZE', TransitionType.PIP_RESIZE],
- ['PIP_EXPAND', TransitionType.PIP_EXPAND],
- ['PIP_EXIT', TransitionType.PIP_EXIT],
- ['APP_LAUNCH', TransitionType.APP_LAUNCH],
- ['APP_CLOSE', TransitionType.APP_CLOSE],
- ['IME_APPEAR', TransitionType.IME_APPEAR],
- ['IME_DISAPPEAR', TransitionType.IME_DISAPPEAR],
- ['APP_PAIRS_ENTER', TransitionType.APP_PAIRS_ENTER],
- ['APP_PAIRS_EXIT', TransitionType.APP_PAIRS_EXIT],
-]);
-
-Tag.fromProto = function (proto: any): Tag {
- const tag = new Tag(
- proto.id,
- transitionTypeMap.get(proto.transition),
- proto.isStartTag,
- proto.layerId,
- proto.windowToken,
- proto.taskId
- );
- return tag;
-};
-
-export default Tag;
diff --git a/tools/winscope/src/flickerlib/tags/TagState.ts b/tools/winscope/src/flickerlib/tags/TagState.ts
deleted file mode 100644
index 7a3de7b..0000000
--- a/tools/winscope/src/flickerlib/tags/TagState.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 { TagState } from "../common";
-import Tag from './Tag';
-
-TagState.fromProto = function (timestamp: number, protos: any[]): TagState {
- const tags = protos.map(it => Tag.fromProto(it));
- const state = new TagState(`${timestamp}`, tags);
- return state;
-}
-
-export default TagState;
diff --git a/tools/winscope/src/flickerlib/tags/TransitionType.ts b/tools/winscope/src/flickerlib/tags/TransitionType.ts
deleted file mode 100644
index ac5d02d..0000000
--- a/tools/winscope/src/flickerlib/tags/TransitionType.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2021, 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 TransitionType {
- ROTATION = 'ROTATION',
- PIP_ENTER = 'PIP_ENTER',
- PIP_RESIZE ='PIP_RESIZE',
- PIP_EXPAND = 'PIP_EXPAND',
- PIP_EXIT = 'PIP_EXIT',
- APP_LAUNCH = 'APP_LAUNCH',
- APP_CLOSE = 'APP_CLOSE',
- IME_APPEAR = 'IME_APPEAR',
- IME_DISAPPEAR = 'IME_DISAPPEAR',
- APP_PAIRS_ENTER = 'APP_PAIRS_ENTER',
- APP_PAIRS_EXIT = 'APP_PAIRS_EXIT',
-};
-
-export default TransitionType;
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/Chip.ts b/tools/winscope/src/flickerlib/treeview/Chip.ts
deleted file mode 100644
index 30f1c83..0000000
--- a/tools/winscope/src/flickerlib/treeview/Chip.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 70c25d7..0000000
--- a/tools/winscope/src/flickerlib/treeview/ChipType.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 23b8e8b..0000000
--- a/tools/winscope/src/flickerlib/treeview/Chips.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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)
-
-export const RELATIVE_Z_CHIP = {
- short: 'RelZ',
- long: 'Is relative Z-ordered to another surface',
- class: 'warn',
-};
-
-export const RELATIVE_Z_PARENT_CHIP = {
- short: 'RelZParent',
- long: 'Something is relative Z-ordered to this surface',
- class: 'warn',
-};
-
-export const MISSING_LAYER = {
- short: 'MissingLayer',
- long: 'This layer was referenced from the parent, but not present in the trace',
- class: 'error',
-};
-
-export const GPU_CHIP = {
- short: 'GPU',
- long: 'This layer was composed on the GPU',
- class: 'gpu',
-};
-
-export const HWC_CHIP = {
- short: 'HWC',
- long: 'This layer was composed by Hardware Composer',
- class: 'hwc',
-};
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/windows/Activity.ts b/tools/winscope/src/flickerlib/windows/Activity.ts
deleted file mode 100644
index 51e8139..0000000
--- a/tools/winscope/src/flickerlib/windows/Activity.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-import { Activity } from "../common"
-import { VISIBLE_CHIP } from '../treeview/Chips'
-import WindowContainer from "./WindowContainer"
-
-Activity.fromProto = function (proto: any): Activity {
- if (proto == null) {
- return null;
- } else {
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.windowToken.windowContainer,
- /* protoChildren */ proto.windowToken.windowContainer?.children?.reverse() ?? [],
- /* isActivityInTree */ true,
- /* nameOverride */ null,
- /* identifierOverride */ proto.identifier
- );
-
- const entry = new Activity(
- proto.name,
- proto.state,
- proto.visible,
- proto.frontOfTask,
- proto.procId,
- proto.translucent,
- windowContainer
- );
-
- addAttributes(entry, proto);
- return entry;
- }
-}
-
-function addAttributes(entry: Activity, proto: any) {
- entry.proto = proto;
- entry.kind = entry.constructor.name;
- entry.shortName = shortenName(entry.name);
- entry.chips = entry.isVisible ? [VISIBLE_CHIP] : [];
-}
-
-export default Activity;
diff --git a/tools/winscope/src/flickerlib/windows/DisplayArea.ts b/tools/winscope/src/flickerlib/windows/DisplayArea.ts
deleted file mode 100644
index 121bb3e..0000000
--- a/tools/winscope/src/flickerlib/windows/DisplayArea.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-import { DisplayArea } from "../common"
-import WindowContainer from "./WindowContainer"
-
-DisplayArea.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayArea {
- if (proto == null) {
- return null;
- } else {
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.windowContainer,
- /* protoChildren */ proto.windowContainer?.children?.reverse() ?? [],
- /* isActivityInTree */ isActivityInTree,
- /* nameOverride */ proto.name
- );
-
- const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer);
-
- addAttributes(entry, proto);
- return entry;
- }
-}
-
-function addAttributes(entry: DisplayArea, proto: any) {
- entry.proto = proto;
- entry.kind = entry.constructor.name;
- entry.shortName = shortenName(entry.name);
-}
-
-export default DisplayArea;
diff --git a/tools/winscope/src/flickerlib/windows/DisplayContent.ts b/tools/winscope/src/flickerlib/windows/DisplayContent.ts
deleted file mode 100644
index 69ac12f..0000000
--- a/tools/winscope/src/flickerlib/windows/DisplayContent.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-import { toRect, DisplayContent, Rect } from "../common"
-import WindowContainer from "./WindowContainer"
-
-DisplayContent.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayContent {
- if (proto == null) {
- return null;
- } else {
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.rootDisplayArea.windowContainer,
- /* protoChildren */ proto.rootDisplayArea.windowContainer?.children?.reverse() ?? [],
- /* isActivityInTree */ isActivityInTree,
- /* nameOverride */ proto.displayInfo?.name ?? null
- );
- const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0;
- const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0;
- const appRectWidth = proto.displayInfo?.appWidth ?? 0;
- const appRectHeight = proto.displayInfo?.appHeight ?? 0;
- const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null;
- const movementBounds = proto.pinnedStackController?.movementBounds ?? null;
-
- const entry = new DisplayContent(
- proto.id,
- proto.focusedRootTaskId,
- proto.resumedActivity?.title ?? "",
- proto.singleTaskInstance,
- toRect(defaultBounds),
- toRect(movementBounds),
- new Rect(0, 0, displayRectWidth, displayRectHeight),
- new Rect(0, 0, appRectWidth, appRectHeight),
- proto.dpi,
- proto.displayInfo?.flags ?? 0,
- toRect(proto.displayFrames?.stableBounds),
- proto.surfaceSize,
- proto.focusedApp,
- proto.appTransition?.lastUsedAppTransition ?? "",
- proto.appTransition?.appTransitionState ?? "",
- proto.displayRotation?.rotation ?? 0,
- proto.displayRotation?.lastOrientation ?? 0,
- windowContainer
- );
-
- addAttributes(entry, proto);
- return entry;
- }
-}
-
-function addAttributes(entry: DisplayContent, proto: any) {
- entry.proto = proto;
- entry.kind = entry.constructor.name;
- entry.shortName = shortenName(entry.name);
-}
-
-export default DisplayContent;
diff --git a/tools/winscope/src/flickerlib/windows/Task.ts b/tools/winscope/src/flickerlib/windows/Task.ts
deleted file mode 100644
index fcf6236..0000000
--- a/tools/winscope/src/flickerlib/windows/Task.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-import { Task, toRect } from "../common"
-import WindowContainer from "./WindowContainer"
-
-Task.fromProto = function (proto: any, isActivityInTree: Boolean): Task {
- if (proto == null) {
- return null;
- } else {
- const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer;
- const windowContainer = WindowContainer.fromProto(
- /* proto */ windowContainerProto,
- /* protoChildren */ windowContainerProto?.children?.reverse() ?? [],
- /* isActivityInTree */ isActivityInTree
- );
-
- const entry = new Task(
- proto.taskFragment?.activityType ?? proto.activityType,
- proto.fillsParent,
- toRect(proto.bounds),
- proto.id,
- proto.rootTaskId,
- proto.taskFragment?.displayId,
- toRect(proto.lastNonFullscreenBounds),
- proto.realActivity,
- proto.origActivity,
- proto.resizeMode,
- proto.resumedActivity?.title ?? "",
- proto.animatingBounds,
- proto.surfaceWidth,
- proto.surfaceHeight,
- proto.createdByOrganizer,
- proto.taskFragment?.minWidth ?? proto.minWidth,
- proto.taskFragment?.minHeight ?? proto.minHeight,
- windowContainer
- );
-
- addAttributes(entry, proto);
- return entry;
- }
-}
-
-function addAttributes(entry: Task, proto: any) {
- entry.proto = proto;
- entry.kind = entry.constructor.name;
- entry.shortName = shortenName(entry.name);
-}
-
-export default Task;
diff --git a/tools/winscope/src/flickerlib/windows/TaskFragment.ts b/tools/winscope/src/flickerlib/windows/TaskFragment.ts
deleted file mode 100644
index 017f760..0000000
--- a/tools/winscope/src/flickerlib/windows/TaskFragment.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2021, 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 { shortenName } from '../mixin'
-import { TaskFragment } from "../common"
-import WindowContainer from "./WindowContainer"
-
-TaskFragment.fromProto = function (proto: any, isActivityInTree: Boolean): TaskFragment {
- if (proto == null) {
- return null;
- } else {
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.windowContainer,
- /* protoChildren */ proto.windowContainer?.children?.reverse() ?? [],
- /* isActivityInTree */ isActivityInTree);
- const entry = new TaskFragment(
- proto.activityType,
- proto.displayId,
- proto.minWidth,
- proto.minHeight,
- windowContainer
- );
-
- addAttributes(entry, proto);
- return entry;
- }
-}
-
-function addAttributes(entry: TaskFragment, proto: any) {
- entry.proto = proto;
- entry.kind = entry.constructor.name;
- entry.shortName = shortenName(entry.name);
-}
-
-export default TaskFragment;
diff --git a/tools/winscope/src/flickerlib/windows/WindowContainer.ts b/tools/winscope/src/flickerlib/windows/WindowContainer.ts
deleted file mode 100644
index 7d71f38..0000000
--- a/tools/winscope/src/flickerlib/windows/WindowContainer.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-
-import {
- Configuration,
- ConfigurationContainer,
- toRect,
- WindowConfiguration,
- WindowContainer,
- WindowContainerChild,
- } from "../common"
-
-import Activity from "./Activity"
-import DisplayArea from "./DisplayArea"
-import DisplayContent from "./DisplayContent"
-import Task from "./Task"
-import TaskFragment from "./TaskFragment"
-import WindowState from "./WindowState"
-import WindowToken from "./WindowToken"
-
-WindowContainer.fromProto = function (
- proto: any,
- protoChildren: any[],
- isActivityInTree: boolean,
- nameOverride: string = null,
- identifierOverride: string = null,
- tokenOverride = null,
-): WindowContainer {
- if (proto == null) {
- return null;
- }
-
- const children = protoChildren
- .filter(it => it != null)
- .map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
- .filter(it => it != null);
-
- const identifier = identifierOverride ?? proto.identifier;
- var name = nameOverride ?? identifier?.title ?? "";
- var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? "";
-
- const config = createConfigurationContainer(proto.configurationContainer);
- const entry = new WindowContainer(
- name,
- token,
- proto.orientation,
- proto.surfaceControl?.layerId ?? 0,
- proto.visible,
- config,
- children
- );
-
- addAttributes(entry, proto);
- return entry;
-}
-
-function addAttributes(entry: WindowContainer, proto: any) {
- entry.proto = proto;
- entry.kind = entry.constructor.name;
- entry.shortName = shortenName(entry.name);
-}
-
-WindowContainer.childrenFromProto = function(proto: any, isActivityInTree: Boolean): WindowContainerChild {
- return DisplayContent.fromProto(proto.displayContent, isActivityInTree) ??
- DisplayArea.fromProto(proto.displayArea, isActivityInTree) ??
- Task.fromProto(proto.task, isActivityInTree) ??
- TaskFragment.fromProto(proto.taskFragment, isActivityInTree) ??
- Activity.fromProto(proto.activity) ??
- WindowToken.fromProto(proto.windowToken, isActivityInTree) ??
- WindowState.fromProto(proto.window, isActivityInTree) ??
- WindowContainer.fromProto(proto.windowContainer);
-}
-
-function createConfigurationContainer(proto: any): ConfigurationContainer {
- const entry = new ConfigurationContainer(
- createConfiguration(proto?.overrideConfiguration ?? null),
- createConfiguration(proto?.fullConfiguration ?? null),
- createConfiguration(proto?.mergedOverrideConfiguration ?? null)
- );
-
- entry.obj = entry;
- return entry;
-}
-
-function createConfiguration(proto: any): Configuration {
- if (proto == null) {
- return null;
- }
- var windowConfiguration = null;
-
- if (proto != null && proto.windowConfiguration != null) {
- windowConfiguration = createWindowConfiguration(proto.windowConfiguration);
- }
-
- return new Configuration(
- windowConfiguration,
- proto?.densityDpi ?? 0,
- proto?.orientation ?? 0,
- proto?.screenHeightDp ?? 0,
- proto?.screenHeightDp ?? 0,
- proto?.smallestScreenWidthDp ?? 0,
- proto?.screenLayout ?? 0,
- proto?.uiMode ?? 0
- );
-}
-
-function createWindowConfiguration(proto: any): WindowConfiguration {
- return new WindowConfiguration(
- toRect(proto.appBounds),
- toRect(proto.bounds),
- toRect(proto.maxBounds),
- proto.windowingMode,
- proto.activityType
- );
-}
-
-export default WindowContainer;
diff --git a/tools/winscope/src/flickerlib/windows/WindowState.ts b/tools/winscope/src/flickerlib/windows/WindowState.ts
deleted file mode 100644
index a9cdf08..0000000
--- a/tools/winscope/src/flickerlib/windows/WindowState.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-import { toRect, Size, WindowState, WindowLayoutParams } from "../common"
-import { VISIBLE_CHIP } from '../treeview/Chips'
-import WindowContainer from "./WindowContainer"
-
- WindowState.fromProto = function (proto: any, isActivityInTree: Boolean): WindowState {
- if (proto == null) {
- return null;
- } else {
- const windowParams = createWindowLayoutParams(proto.attributes);
- const identifierName = getIdentifier(proto);
- const windowType = getWindowType(proto, identifierName);
- const name = getName(identifierName);
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.windowContainer,
- /* protoChildren */ proto.windowContainer?.children.reverse() ?? [],
- /* isActivityInTree */ isActivityInTree,
- /* nameOverride */ name,
- /* identifierOverride */ proto.identifier
- );
-
- const entry = new WindowState(
- windowParams,
- proto.displayId,
- proto.stackId,
- proto.animator?.surface?.layer ?? 0,
- proto.animator?.surface?.shown ?? false,
- windowType,
- new Size(proto.requestedWidth, proto.requestedHeight),
- toRect(proto.surfacePosition),
- toRect(proto.windowFrames?.frame ?? null),
- toRect(proto.windowFrames?.containingFrame ?? null),
- toRect(proto.windowFrames?.parentFrame ?? null),
- toRect(proto.windowFrames?.contentFrame ?? null),
- toRect(proto.windowFrames?.contentInsets ?? null),
- toRect(proto.surfaceInsets),
- toRect(proto.givenContentInsets),
- toRect(proto.animator?.lastClipRect ?? null),
- windowContainer,
- /* isAppWindow */ isActivityInTree
- );
-
- addAttributes(entry, proto);
- return entry;
- }
-}
-
-function createWindowLayoutParams(proto: any): WindowLayoutParams {
- return new WindowLayoutParams(
- /* type */ proto?.type ?? 0,
- /* x */ proto?.x ?? 0,
- /* y */ proto?.y ?? 0,
- /* width */ proto?.width ?? 0,
- /* height */ proto?.height ?? 0,
- /* horizontalMargin */ proto?.horizontalMargin ?? 0,
- /* verticalMargin */ proto?.verticalMargin ?? 0,
- /* gravity */ proto?.gravity ?? 0,
- /* softInputMode */ proto?.softInputMode ?? 0,
- /* format */ proto?.format ?? 0,
- /* windowAnimations */ proto?.windowAnimations ?? 0,
- /* alpha */ proto?.alpha ?? 0,
- /* screenBrightness */ proto?.screenBrightness ?? 0,
- /* buttonBrightness */ proto?.buttonBrightness ?? 0,
- /* rotationAnimation */ proto?.rotationAnimation ?? 0,
- /* preferredRefreshRate */ proto?.preferredRefreshRate ?? 0,
- /* preferredDisplayModeId */ proto?.preferredDisplayModeId ?? 0,
- /* hasSystemUiListeners */ proto?.hasSystemUiListeners ?? false,
- /* inputFeatureFlags */ proto?.inputFeatureFlags ?? 0,
- /* userActivityTimeout */ proto?.userActivityTimeout ?? 0,
- /* colorMode */ proto?.colorMode ?? 0,
- /* flags */ proto?.flags ?? 0,
- /* privateFlags */ proto?.privateFlags ?? 0,
- /* systemUiVisibilityFlags */ proto?.systemUiVisibilityFlags ?? 0,
- /* subtreeSystemUiVisibilityFlags */ proto?.subtreeSystemUiVisibilityFlags ?? 0,
- /* appearance */ proto?.appearance ?? 0,
- /* behavior */ proto?.behavior ?? 0,
- /* fitInsetsTypes */ proto?.fitInsetsTypes ?? 0,
- /* fitInsetsSides */ proto?.fitInsetsSides ?? 0,
- /* fitIgnoreVisibility */ proto?.fitIgnoreVisibility ?? false
- )
-}
-
-function getWindowType(proto: any, identifierName: string): number {
- if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
- return WindowState.WINDOW_TYPE_STARTING;
- } else if (proto.animatingExit) {
- return WindowState.WINDOW_TYPE_EXITING;
- } else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
- return WindowState.WINDOW_TYPE_STARTING;
- }
-
- return 0;
-}
-
-function getName(identifierName: string): string {
- var name = identifierName;
-
- if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
- name = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length);
- } else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
- name = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length);
- }
-
- return name;
-}
-
-function getIdentifier(proto: any): string {
- return proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? "";
-}
-
-function addAttributes(entry: WindowState, proto: any) {
- entry.kind = entry.constructor.name;
- entry.rect = entry.frame;
- entry.rect.ref = entry;
- entry.rect.label = entry.name;
- entry.proto = proto;
- entry.shortName = shortenName(entry.name);
- entry.chips = entry.isVisible ? [VISIBLE_CHIP] : [];
-}
-
-export default WindowState
diff --git a/tools/winscope/src/flickerlib/windows/WindowToken.ts b/tools/winscope/src/flickerlib/windows/WindowToken.ts
deleted file mode 100644
index 2e50e1d..0000000
--- a/tools/winscope/src/flickerlib/windows/WindowToken.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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 { shortenName } from '../mixin'
-import { WindowToken } from "../common"
-import WindowContainer from "./WindowContainer"
-
-WindowToken.fromProto = function (proto: any, isActivityInTree: Boolean): WindowToken {
- if (proto == null) {
- return null;
- }
-
- const windowContainer = WindowContainer.fromProto(
- /* proto */ proto.windowContainer,
- /* protoChildren */ proto.windowContainer?.children?.reverse() ?? [],
- /* isActivityInTree */ isActivityInTree,
- /* nameOverride */ null,
- /* identifierOverride */ null,
- /* tokenOverride */ proto.hashCode
- );
- const entry = new WindowToken(windowContainer);
- entry.kind = entry.constructor.name;
- entry.proto = proto;
- entry.shortName = shortenName(entry.name);
- return entry;
-}
-
-export default WindowToken;
diff --git a/tools/winscope/src/index.html b/tools/winscope/src/index.html
new file mode 100644
index 0000000..4a9bd1d
--- /dev/null
+++ b/tools/winscope/src/index.html
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2022 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.
+-->
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Winscope</title>
+ <base href="/">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/x-icon" href="favicon.svg">
+</head>
+<body class="mat-app-background">
+ <app-root></app-root>
+</body>
+</html>
diff --git a/tools/winscope/src/index_template.html b/tools/winscope/src/index_template.html
deleted file mode 100644
index a7168a1..0000000
--- a/tools/winscope/src/index_template.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<!-- Copyright (C) 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.
--->
-<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>
- </head>
- <body>
- <div id="app"></div>
- </body>
-</html>
diff --git a/tools/winscope/src/interfaces/buganizer_attachments_download_emitter.ts b/tools/winscope/src/interfaces/buganizer_attachments_download_emitter.ts
new file mode 100644
index 0000000..f62e57b
--- /dev/null
+++ b/tools/winscope/src/interfaces/buganizer_attachments_download_emitter.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 type OnBuganizerAttachmentsDownloadStart = () => void;
+export type OnBuganizerAttachmentsDownloaded = (attachments: File[]) => Promise<void>;
+
+export interface BuganizerAttachmentsDownloadEmitter {
+ setOnBuganizerAttachmentsDownloadStart(callback: OnBuganizerAttachmentsDownloadStart): void;
+ setOnBuganizerAttachmentsDownloaded(callback: OnBuganizerAttachmentsDownloaded): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/progress_listener.ts
similarity index 72%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/progress_listener.ts
index bfe19ce..bae51a2 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/progress_listener.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface ProgressListener {
+ onProgressUpdate(message: string, progressPercentage: number | undefined): void;
+ onOperationFinished(): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/progress_listener_stub.ts
similarity index 66%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/progress_listener_stub.ts
index bfe19ce..57c723e 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/progress_listener_stub.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {ProgressListener} from 'interfaces/progress_listener';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class ProgressListenerStub implements ProgressListener {
+ onProgressUpdate() {
+ // do nothing
+ }
+
+ onOperationFinished() {
+ // do nothing
+ }
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/remote_bugreport_receiver.ts
similarity index 64%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/remote_bugreport_receiver.ts
index bfe19ce..992eef7 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/remote_bugreport_receiver.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {RealTimestamp} from 'trace/timestamp';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export type OnBugreportReceived = (bugreport: File, timestamp?: RealTimestamp) => Promise<void>;
+
+export interface RemoteBugreportReceiver {
+ setOnBugreportReceived(callback: OnBugreportReceived): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/remote_timestamp_receiver.ts
similarity index 66%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/remote_timestamp_receiver.ts
index bfe19ce..e25b470 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/remote_timestamp_receiver.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {RealTimestamp} from 'trace/timestamp';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export type OnTimestampReceived = (timestamp: RealTimestamp) => void;
+
+export interface RemoteTimestampReceiver {
+ setOnTimestampReceived(callback: OnTimestampReceived): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/remote_timestamp_sender.ts
similarity index 74%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/remote_timestamp_sender.ts
index bfe19ce..03ab553 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/remote_timestamp_sender.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {RealTimestamp} from 'trace/timestamp';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface RemoteTimestampSender {
+ sendTimestamp(timestamp: RealTimestamp): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/runnable.ts
similarity index 76%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/runnable.ts
index bfe19ce..e14beb8 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/runnable.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface Runnable {
+ run(): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/trace_data_listener.ts
similarity index 72%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/trace_data_listener.ts
index bfe19ce..3efc14b 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/trace_data_listener.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {Viewer} from 'viewers/viewer';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface TraceDataListener {
+ onTraceDataUnloaded(): void;
+ onTraceDataLoaded(viewers: Viewer[]): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/trace_position_update_listener.ts
similarity index 72%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/trace_position_update_listener.ts
index bfe19ce..571f3ef 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/trace_position_update_listener.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {TracePosition} from 'trace/trace_position';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface TracePositionUpdateListener {
+ onTracePositionUpdate(position: TracePosition): void;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/interfaces/user_notification_listener.ts
similarity index 73%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/interfaces/user_notification_listener.ts
index bfe19ce..d2b1744 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/interfaces/user_notification_listener.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {ParserError} from 'parsers/parser_factory';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface UserNotificationListener {
+ onParserErrors(errors: ParserError[]): void;
+}
diff --git a/tools/winscope/src/localstore.js b/tools/winscope/src/localstore.js
deleted file mode 100644
index cf4336a..0000000
--- a/tools/winscope/src/localstore.js
+++ /dev/null
@@ -1,37 +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 Vue from 'vue';
-
-function Store(name, data) {
- return new Vue({
- data,
- created() {
- Object.keys(this.$data).forEach((k) =>{
- var stored = window.localStorage.getItem(name + '.' + k);
- if (stored) {
- this.$data[k] = JSON.parse(stored);
- }
- this.$watch(k, () => {
- window.localStorage.setItem(name + '.' + k,
- JSON.stringify(this.$data[k]));
- }, {deep: true});
- });
- }
- });
-}
-
-export default Store;
diff --git a/tools/winscope/src/main.js b/tools/winscope/src/main.js
deleted file mode 100644
index 587f425..0000000
--- a/tools/winscope/src/main.js
+++ /dev/null
@@ -1,440 +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 Vue from 'vue'
-import Vuex from 'vuex'
-import VueMaterial from 'vue-material'
-import VueGtag from "vue-gtag";
-
-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 '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_LEGACY]: 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,
- TRACE_TYPES.TAG,
- TRACE_TYPES.ERROR
- ],
- 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));
- },
- tagFiles(state, getters) {
- return Object.values(state.traces)
- .filter(file => file.type === TRACE_TYPES.TAG);
- },
- errorFiles(state, getters) {
- return Object.values(state.traces)
- .filter(file => file.type === TRACE_TYPES.ERROR);
- },
- sortedTimelineFiles(state, getters) {
- return sortFiles(getters.timelineFiles);
- },
- video(state) {
- return state.traces[TRACE_TYPES.SCREEN_RECORDING];
- },
- tagGenerationWmTrace(state, getters) {
- return state.traces[TRACE_TYPES.WINDOW_MANAGER].tagGenerationTrace;
- },
- tagGenerationSfTrace(state, getters) {
- return state.traces[TRACE_TYPES.SURFACE_FLINGER].tagGenerationTrace;
- }
- },
- 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) {
- const newObj = new typeInfo.constructor(traceFiles);
- newObj.data = Object.freeze(newObj.data);
- traces[traceType] = newObj;
- }
- }
-
- 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) {
- const newObj = new typeInfo.constructor(dumpFiles);
- newObj.data = Object.freeze(newObj.data);
- dumps[dumpType] = newObj;
- }
-
- }
-
- 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) {
- //dumps do not have a timeline, so only look at files with timelines to update the timestamp
- if (!file.timeline) continue;
-
- 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);
- }
- }
- }
-})
-
-/**
- * Make Google analytics functionalities available for recording events.
- */
-Vue.use(VueGtag, {
- config: { id: 'G-RRV0M08Y76'}
-})
-
-Vue.mixin({
- methods: {
- recordButtonClickedEvent(button) {
- const txt = "Clicked " + button + " Button";
- this.$gtag.event(txt, {
- 'event_category': 'Button Clicked',
- 'event_label': "Winscope Interactions",
- 'value': button,
- });
- },
- recordDragAndDropFileEvent(val) {
- this.$gtag.event("Dragged And DroppedFile", {
- 'event_category': 'Uploaded file',
- 'event_label': "Winscope Interactions",
- 'value': val,
- });
- },
- recordFileUploadEvent(val) {
- this.$gtag.event("Uploaded File From Filesystem", {
- 'event_category': 'Uploaded file',
- 'event_label': "Winscope Interactions",
- 'value': val,
- });
- },
- recordNewEvent(event) {
- this.$gtag.event(event, {
- 'event_category': event,
- 'event_label': "Winscope Interactions",
- 'value': 1,
- });
- },
- recordOpenTraceEvent(traceType) {
- this.$gtag.screenview({
- app_name: "Winscope",
- screen_name: traceType,
- })
- },
- recordExpandedPropertyEvent(field) {
- const string = "Property: " + field;
- this.$gtag.event(string, {
- 'event_category': "Expanded property",
- 'event_label': "Winscope Interactions",
- 'value': field,
- });
- },
- recordOpenedEntryEvent(entryType) {
- const string = "Trace: " + entryType;
- this.$gtag.event(string, {
- 'event_category': "Opened trace",
- 'event_label': "Winscope Interactions",
- 'value': entryType,
- });
- },
- recordChangedNavigationStyleEvent(field) {
- this.$gtag.event("Navigation mode changed", {
- 'event_category': "Timeline Navigation",
- 'event_label': "Winscope Interactions",
- 'value': field,
- });
- },
- }
-});
-
-new Vue({
- el: '#app',
- store, // inject the Vuex store into all components
- render: h => h(App)
-})
diff --git a/tools/winscope/src/main_component_test.ts b/tools/winscope/src/main_component_test.ts
new file mode 100644
index 0000000..f6c90fd
--- /dev/null
+++ b/tools/winscope/src/main_component_test.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// organize-imports-ignore
+import 'zone.js';
+import 'zone.js/testing';
+import {TestBed} from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting,
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+ context(
+ path: string,
+ deep?: boolean,
+ filter?: RegExp
+ ): {
+ <T>(id: string): T;
+ keys(): string[];
+ };
+};
+
+TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
+
+// load all tests of Angular components
+const context = require.context('./', true, /_component_test\.ts$/);
+context.keys().forEach(context);
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/main_dev.ts
similarity index 61%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/main_dev.ts
index bfe19ce..1e69894 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/main_dev.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
+import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
+import {globalConfig} from 'common/global_config';
+import {AppModule} from './app/app_module';
-package org.chromium.arc.wayland_composer;
+globalConfig.set({
+ MODE: 'DEV',
+});
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+platformBrowserDynamic()
+ .bootstrapModule(AppModule)
+ .catch((err) => console.error(err));
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/main_prod.ts
similarity index 62%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/main_prod.ts
index bfe19ce..52ce2f5 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/main_prod.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
+import {enableProdMode} from '@angular/core';
+import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
+import {AppModule} from './app/app_module';
-package org.chromium.arc.wayland_composer;
+enableProdMode();
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+platformBrowserDynamic()
+ .bootstrapModule(AppModule)
+ .catch((err) => console.error(err));
diff --git a/tools/winscope/src/material-theme.scss b/tools/winscope/src/material-theme.scss
new file mode 100644
index 0000000..ddb3367
--- /dev/null
+++ b/tools/winscope/src/material-theme.scss
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 'sass:map';
+@use '@angular/material' as mat;
+
+@import 'https://fonts.googleapis.com/icon?family=Material+Icons';
+@import '//fonts.googleapis.com/css2?family=Google+Sans';
+
+$typography: mat.define-typography-config(
+ $font-family: 'Roboto, sans-serif'
+);
+
+$primary: mat.define-palette(mat.$blue-palette, 700);
+$accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
+$warn: mat.define-palette(mat.$red-palette);
+
+$light-theme: mat.define-light-theme((
+ color: (
+ primary: $primary,
+ accent: $accent,
+ warn: $warn,
+ ),
+ density: 0,
+ typography: null, // Set typography on mat.core() only, to avoid duplicates.
+));
+
+$dark-theme: mat.define-dark-theme((
+ color: (
+ primary: $primary,
+ accent: $accent,
+ warn: $warn,
+ )
+));
+
+@mixin border-color($theme) {
+ $color: mat.get-color-config($theme);
+ $foreground: map.get($color, 'foreground');
+
+ & {
+ --border-color: #{mat.get-color-from-palette($foreground, divider)};
+ }
+}
+
+@include mat.core($typography);
+@include mat.all-component-themes($light-theme);
+
+body:not(.dark-mode) {
+ @include border-color($light-theme);
+}
+
+body.dark-mode {
+ @include mat.all-component-colors($dark-theme);
+ @include border-color($dark-theme);
+}
diff --git a/tools/winscope/src/matrix_utils.js b/tools/winscope/src/matrix_utils.js
deleted file mode 100644
index 55fcfec..0000000
--- a/tools/winscope/src/matrix_utils.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 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.
- */
-
-function multiplyVec2(matrix, x, y) {
- if (!matrix) return {x, y};
- // |dsdx dsdy tx| | x |
- // |dtdx dtdy ty| x | y |
- // |0 0 1 | | 1 |
- return {
- x: matrix.dsdx * x + matrix.dsdy * y + matrix.tx,
- y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty,
- };
-}
-
-function multiplyRect(transform, rect) {
- let matrix = transform;
- if (transform && transform.matrix) {
- matrix = transform.matrix;
- }
- // |dsdx dsdy tx| | left, top |
- // matrix = |dtdx dtdy ty| rect = | |
- // |0 0 1 | | right, bottom |
-
- const leftTop = multiplyVec2(matrix, rect.left, rect.top);
- const rightTop = multiplyVec2(matrix, rect.right, rect.top);
- const leftBottom = multiplyVec2(matrix, rect.left, rect.bottom);
- const rightBottom = multiplyVec2(matrix, rect.right, rect.bottom);
-
- const outrect = {};
- outrect.left = Math.min(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
- outrect.top = Math.min(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y);
- outrect.right = Math.max(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
- outrect.bottom = Math.max(leftTop.y, rightTop.y, leftBottom.y,
- rightBottom.y);
- return outrect;
-}
-
-export {multiplyRect};
diff --git a/tools/winscope/src/mixins/FileType.js b/tools/winscope/src/mixins/FileType.js
deleted file mode 100644
index a416135..0000000
--- a/tools/winscope/src/mixins/FileType.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.ACCESSIBILITY ||
- file.type == TRACE_TYPES.SURFACE_FLINGER ||
- file.type == TRACE_TYPES.TRANSACTION ||
- 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;
- },
- showInAccessibilityTraceView(file) {
- return file.type == TRACE_TYPES.ACCESSIBILITY;
- },
- 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;
- },
- isTransactionsLegacy(file) {
- return file.type == TRACE_TYPES.TRANSACTION_LEGACY;
- },
- isLog(file) {
- return file.type == TRACE_TYPES.PROTO_LOG;
- },
- hasDataView(file) {
- return this.isLog(file) || this.showInTraceView(file) ||
- this.isTransactionsLegacy(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
deleted file mode 100644
index 27f341f..0000000
--- a/tools/winscope/src/mixins/FocusedDataViewFinder.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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
deleted file mode 100644
index e85feec..0000000
--- a/tools/winscope/src/mixins/SaveAsZip.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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);
- },
- /**
- * Returns the file name, if the file has an extension use its default,
- * otherwise use ".mp4" for screen recording (name from proxy script) and
- * ".winscope" for traces
- * @param {*} fileName
- */
- getFileName(fileName) {
- var re = /(?:\.([^.]+))?$/;
- var extension = re.exec(fileName)[1];
- if (!extension) {
- extension = "";
- }
- switch (extension) {
- case "": {
- if (fileName == "Screen recording") {
- return fileName + ".mp4"
- }
- return fileName + ".winscope"
- }
- default: return fileName
- }
- },
- async downloadAsZip(traces, traceName='winscope') {
- const zip = new JSZip();
- this.recordButtonClickedEvent("Download All")
-
- for (const trace of traces) {
- const traceFolder = zip.folder(trace.type);
- for (const file of trace.files) {
- var fileName = this.getFileName(file.filename);
- const blob = await fetch(file.blobUrl).then((r) => r.blob());
- traceFolder.file(fileName, blob);
- }
- }
-
- const zipFile = await zip.generateAsync({type: 'blob'});
-
- this.saveAs(zipFile, `${traceName}.zip`);
- },
- },
-};
diff --git a/tools/winscope/src/mixins/Timeline.js b/tools/winscope/src/mixins/Timeline.js
deleted file mode 100644
index 444d525..0000000
--- a/tools/winscope/src/mixins/Timeline.js
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * 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 _ from "lodash";
-import { nanos_to_string } from "../transform";
-import { transitionMap } from "../utils/consts";
-
-/**
- * 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;
- }
-}
-
-//Represents a continuous section of the tag display that relates to a specific transition
-class Transition {
- /**
- * Create a transition.
- * @param {number} startPos - The position of the start tag as a percentage
- * of the timeline width.
- * @param {number} startTime - The start timestamp in ms of the transition.
- * @param {number} endTime - The end timestamp in ms of the transition.
- * @param {number} width - The width of the transition as a percentage of the
- * timeline width.
- * @param {string} color - the color of transition depending on type.
- * @param {number} overlap - number of transitions with which this transition overlaps.
- * @param {string} tooltip - The tooltip of the transition, minus the type of transition.
- */
- constructor(startPos, startTime, endTime, width, color, overlap, tooltip) {
- this.startPos = startPos;
- this.startTime = startTime;
- this.endTime = endTime;
- this.width = width;
- this.color = color;
- this.overlap = overlap;
- this.tooltip = tooltip;
- }
-}
-
-/**
- * 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,
- },
- 'tags': {
- type: Array,
- },
- 'errors': {
- type: Array,
- },
- 'flickerMode': {
- type: Boolean,
- }
- },
- 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);
- },
-
- //Generates list of transitions to be displayed in flicker mode
- timelineTransitions() {
- const transitions = [];
-
- //group tags by transition and 'id' property
- const groupedTags = _.groupBy(this.tags, tag => `"${tag.transition} ${tag.id}"`);
-
- for (const transitionId in groupedTags) {
- const id = groupedTags[transitionId];
- //there are at least two tags per id, maybe more if multiple traces
- // determine which tag is the start (min of start times), which is end (max of end times)
- const startTimes = id.filter(tag => tag.isStartTag).map(tag => tag.timestamp);
- const endTimes = id.filter(tag => !tag.isStartTag).map(tag => tag.timestamp);
-
- const transitionStartTime = Math.min(...startTimes);
- const transitionEndTime = Math.max(...endTimes);
-
- //do not freeze new transition, as overlap still to be handled (defaulted to 0)
- const transition = this.generateTransition(
- transitionStartTime,
- transitionEndTime,
- id[0].transition,
- 0,
- id[0].layerId,
- id[0].taskId,
- id[0].windowToken
- );
- transitions.push(transition);
- }
-
- //sort transitions in ascending start position in order to handle overlap
- transitions.sort((a, b) => (a.startPos > b.startPos) ? 1 : -1);
-
- //compare each transition to the ones that came before
- for (let curr=0; curr<transitions.length; curr++) {
- let processedTransitions = [];
-
- for (let prev=0; prev<curr; prev++) {
- processedTransitions.push(transitions[prev]);
-
- if (this.isSimultaneousTransition(transitions[curr], transitions[prev])) {
- transitions[curr].overlap++;
- }
- }
-
- let overlapStore = processedTransitions.map(transition => transition.overlap);
-
- if (transitions[curr].overlap === Math.max(...overlapStore)) {
- let previousTransition = processedTransitions.find(transition => {
- return transition.overlap===transitions[curr].overlap;
- });
- if (this.isSimultaneousTransition(transitions[curr], previousTransition)) {
- transitions[curr].overlap++;
- }
- }
- }
-
- return Object.freeze(transitions);
- },
- errorPositions() {
- if (!this.flickerMode) return [];
- const errorPositions = this.errors.map(
- error => ({ pos: this.position(error.timestamp), ts: error.timestamp })
- );
- return Object.freeze(errorPositions);
- },
- },
- 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;
- },
-
- objectWidth(startTs, endTs) {
- return this.position(endTs) - this.position(startTs) + this.pointWidth;
- },
-
- isSimultaneousTransition(currTransition, prevTransition) {
- return prevTransition.startPos <= currTransition.startPos
- && currTransition.startPos <= prevTransition.startPos+prevTransition.width
- && currTransition.overlap === prevTransition.overlap;
- },
-
- /**
- * 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;
- }
-
- var timestamp = parseInt(this.timeline[clickedOnTsIndex]);
-
- //pointWidth is always 1
- //if offset percentage < 1, clickedOnTsIndex becomes negative, leading to a negative index
- if (clickedOnTsIndex < 0) {
- timestamp = parseInt(this.timeline[0])
- }
- this.$store.dispatch('updateTimelineTime', timestamp);
- },
-
- /**
- * Handles the error click event.
- * When an error in the timeline is clicked this function will update the timeline
- * to match the error timestamp.
- * @param {number} errorTimestamp
- */
- onErrorClick(errorTimestamp) {
- this.$store.dispatch('updateTimelineTime', errorTimestamp);
- },
-
- /**
- * 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.objectWidth(startTs, endTs);
- return Object.freeze(new Block(this.position(startTs), blockWidth));
- },
- /**
- * Generate a transition object that can be used by the tag-timeline to render
- * a transformed transition that starts at `startTs` and ends at `endTs`.
- * @param {number} startTs - The timestamp at which the transition starts.
- * @param {number} endTs - The timestamp at which the transition ends.
- * @param {string} transitionType - The type of transition.
- * @param {number} overlap - The degree to which the transition overlaps with others.
- * @param {number} layerId - Helps determine if transition is associated with SF trace.
- * @param {number} taskId - Helps determine if transition is associated with WM trace.
- * @param {number} windowToken - Helps determine if transition is associated with WM trace.
- * @return {Transition} A transition object transformed to the timeline's crop and
- * scale parameter.
- */
- generateTransition(startTs, endTs, transitionType, overlap, layerId, taskId, windowToken) {
- const transitionWidth = this.objectWidth(startTs, endTs);
- const transitionDesc = transitionMap.get(transitionType).desc;
- const transitionColor = transitionMap.get(transitionType).color;
- var tooltip = `${transitionDesc}. Start: ${nanos_to_string(startTs)}. End: ${nanos_to_string(endTs)}.`;
-
- if (layerId !== 0 && taskId === 0 && windowToken === "") {
- tooltip += " SF only.";
- } else if ((taskId !== 0 || windowToken !== "") && layerId === 0) {
- tooltip += " WM only.";
- }
-
- return new Transition(this.position(startTs), startTs, endTs, transitionWidth, transitionColor, overlap, tooltip);
- },
- },
-};
\ No newline at end of file
diff --git a/tools/winscope/src/parsers/abstract_parser.ts b/tools/winscope/src/parsers/abstract_parser.ts
new file mode 100644
index 0000000..6063742
--- /dev/null
+++ b/tools/winscope/src/parsers/abstract_parser.ts
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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 {ArrayUtils} from 'common/array_utils';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+
+abstract class AbstractParser<T extends object = object> implements Parser<object> {
+ protected traceFile: TraceFile;
+ protected decodedEntries: any[] = [];
+ private timestamps: Map<TimestampType, Timestamp[]> = new Map<TimestampType, Timestamp[]>();
+
+ protected constructor(trace: TraceFile) {
+ this.traceFile = trace;
+ }
+
+ async parse() {
+ const traceBuffer = new Uint8Array(await this.traceFile.file.arrayBuffer());
+
+ const magicNumber = this.getMagicNumber();
+ if (magicNumber !== undefined) {
+ const bufferContainsMagicNumber = ArrayUtils.equal(
+ magicNumber,
+ traceBuffer.slice(0, magicNumber.length)
+ );
+ if (!bufferContainsMagicNumber) {
+ throw TypeError("buffer doesn't contain expected magic number");
+ }
+ }
+
+ this.decodedEntries = this.decodeTrace(traceBuffer).map((it) => this.addDefaultProtoFields(it));
+
+ for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
+ const timestamps: Timestamp[] = [];
+ let areTimestampsValid = true;
+
+ for (const entry of this.decodedEntries) {
+ const timestamp = this.getTimestamp(type, entry);
+ if (timestamp === undefined) {
+ areTimestampsValid = false;
+ break;
+ }
+ timestamps.push(timestamp);
+ }
+
+ if (areTimestampsValid) {
+ this.timestamps.set(type, timestamps);
+ }
+ }
+ }
+
+ abstract getTraceType(): TraceType;
+
+ getDescriptors(): string[] {
+ return [this.traceFile.getDescriptor()];
+ }
+
+ getLengthEntries(): number {
+ return this.decodedEntries.length;
+ }
+
+ getTimestamps(type: TimestampType): undefined | Timestamp[] {
+ return this.timestamps.get(type);
+ }
+
+ getEntry(index: number, timestampType: TimestampType): T {
+ return this.processDecodedEntry(index, timestampType, this.decodedEntries[index]);
+ }
+
+ // Add default values to the proto objects.
+ private addDefaultProtoFields(protoObj: any): any {
+ if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) {
+ return protoObj;
+ }
+
+ for (const fieldName in protoObj.$type.fields) {
+ if (Object.prototype.hasOwnProperty.call(protoObj.$type.fields, fieldName)) {
+ const fieldProperties = protoObj.$type.fields[fieldName];
+ const field = protoObj[fieldName];
+
+ if (Array.isArray(field)) {
+ field.forEach((item, _) => {
+ this.addDefaultProtoFields(item);
+ });
+ continue;
+ }
+
+ if (!field) {
+ protoObj[fieldName] = fieldProperties.defaultValue;
+ }
+
+ if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
+ protoObj[fieldName] =
+ fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
+ continue;
+ }
+ this.addDefaultProtoFields(protoObj[fieldName]);
+ }
+ }
+
+ return protoObj;
+ }
+
+ protected abstract getMagicNumber(): undefined | number[];
+ protected abstract decodeTrace(trace: Uint8Array): any[];
+ protected abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined | Timestamp;
+ protected abstract processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ decodedEntry: any
+ ): any;
+}
+
+export {AbstractParser};
diff --git a/tools/winscope/src/parsers/abstract_traces_parser.ts b/tools/winscope/src/parsers/abstract_traces_parser.ts
new file mode 100644
index 0000000..66a8b1f
--- /dev/null
+++ b/tools/winscope/src/parsers/abstract_traces_parser.ts
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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 {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+
+export abstract class AbstractTracesParser<T> implements Parser<T> {
+ constructor(readonly parsers: Array<Parser<object>>) {}
+
+ getTraceFile(): TraceFile {
+ throw new Error('Method not implemented.');
+ }
+
+ abstract canProvideEntries(): boolean;
+
+ abstract getDescriptors(): string[];
+
+ abstract getTraceType(): TraceType;
+
+ abstract getEntry(index: number, timestampType: TimestampType): T;
+
+ abstract getLengthEntries(): number;
+
+ getTimestamps(type: TimestampType): Timestamp[] | undefined {
+ this.setTimestamps();
+ return this.timestamps.get(type);
+ }
+
+ private setTimestamps() {
+ if (this.timestampsSet) {
+ return;
+ }
+
+ for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
+ const timestamps: Timestamp[] = [];
+ let areTimestampsValid = true;
+
+ for (let index = 0; index < this.getLengthEntries(); index++) {
+ const entry = this.getEntry(index, type);
+ const timestamp = this.getTimestamp(type, entry);
+ if (timestamp === undefined) {
+ areTimestampsValid = false;
+ break;
+ }
+ timestamps.push(timestamp);
+ }
+
+ if (areTimestampsValid) {
+ this.timestamps.set(type, timestamps);
+ }
+ }
+
+ this.timestampsSet = true;
+ }
+
+ abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined | Timestamp;
+
+ private timestampsSet: boolean = false;
+ private timestamps: Map<TimestampType, Timestamp[]> = new Map<TimestampType, Timestamp[]>();
+}
diff --git a/tools/winscope/src/parsers/parser_accessibility.ts b/tools/winscope/src/parsers/parser_accessibility.ts
new file mode 100644
index 0000000..fb30419
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_accessibility.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {AccessibilityTraceFileProto} from './proto_types';
+
+class ParserAccessibility extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.ACCESSIBILITY;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserAccessibility.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decoded = AccessibilityTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
+ } else {
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decoded.entry;
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos)
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any {
+ return entryProto;
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
+}
+
+export {ParserAccessibility};
diff --git a/tools/winscope/src/parsers/parser_accessibility_test.ts b/tools/winscope/src/parsers/parser_accessibility_test.ts
new file mode 100644
index 0000000..722162f
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_accessibility_test.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserAccessibility', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/Accessibility.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 14499089524n),
+ new Timestamp(TimestampType.ELAPSED, 14499599656n),
+ new Timestamp(TimestampType.ELAPSED, 14953120693n),
+ ];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1659107089100052652n),
+ new Timestamp(TimestampType.REAL, 1659107089100562784n),
+ new Timestamp(TimestampType.REAL, 1659107089554083821n),
+ ];
+ expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1659107089100562784n);
+ expect(BigInt(parser.getEntry(1, TimestampType.REAL).elapsedRealtimeNanos)).toEqual(
+ 14499599656n
+ );
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/Accessibility.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)![0]).toEqual(
+ new Timestamp(TimestampType.ELAPSED, 850297444302n)
+ );
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_common_test.ts b/tools/winscope/src/parsers/parser_common_test.ts
new file mode 100644
index 0000000..ff1fc03
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_common_test.ts
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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 {CommonTestUtils} from 'test/common/utils';
+import {UnitTestUtils} from 'test/unit/utils';
+import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {ParserFactory} from './parser_factory';
+
+describe('Parser', () => {
+ it('is robust to empty trace file', async () => {
+ const trace = new TraceFile(await CommonTestUtils.getFixtureFile('traces/empty.pb'), undefined);
+ const [parsers, errors] = await new ParserFactory().createParsers([trace]);
+ expect(parsers.length).toEqual(0);
+ });
+
+ it('is robust to trace with no entries', async () => {
+ const parser = await UnitTestUtils.getParser('traces/no_entries_InputMethodClients.pb');
+
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual([]);
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual([]);
+ });
+
+ describe('real timestamp', () => {
+ let parser: Parser<WindowManagerState>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/WindowManager.pb');
+ });
+
+ it('provides timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1659107089075566202n),
+ new Timestamp(TimestampType.REAL, 1659107089999048990n),
+ new Timestamp(TimestampType.REAL, 1659107090010194213n),
+ ];
+ expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('retrieves trace entries', () => {
+ expect(
+ BigInt(parser.getEntry(0, TimestampType.REAL)!.timestamp.unixNanos.toString())
+ ).toEqual(1659107089075566202n);
+ expect(
+ BigInt(
+ parser
+ .getEntry(parser.getLengthEntries() - 1, TimestampType.REAL)!
+ .timestamp.unixNanos.toString()
+ )
+ ).toEqual(1659107091700249187n);
+ });
+ });
+
+ describe('elapsed timestamp', () => {
+ let parser: Parser<WindowManagerState>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/WindowManager.pb');
+ });
+
+ it('provides timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 850254319343n),
+ new Timestamp(TimestampType.ELAPSED, 850763506110n),
+ new Timestamp(TimestampType.ELAPSED, 850782750048n),
+ ];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ });
+
+ it('retrieves trace entries', () => {
+ expect(
+ BigInt(parser.getEntry(0, TimestampType.ELAPSED)!.timestamp.elapsedNanos.toString())
+ ).toEqual(850254319343n);
+ expect(
+ BigInt(
+ parser
+ .getEntry(parser.getLengthEntries() - 1, TimestampType.ELAPSED)!
+ .timestamp.elapsedNanos.toString()
+ )
+ ).toEqual(850782750048n);
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_eventlog.ts b/tools/winscope/src/parsers/parser_eventlog.ts
new file mode 100644
index 0000000..902e80c
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_eventlog.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 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 {Event, EventLogParser} from 'trace/flickerlib/common';
+import {RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+
+class ParserEventLog extends AbstractParser<Event> {
+ override getTraceType(): TraceType {
+ return TraceType.EVENT_LOG;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserEventLog.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): Event[] {
+ const eventLog = EventLogParser.prototype.parse(buffer);
+
+ return eventLog.entries;
+ }
+
+ override getTimestamp(type: TimestampType, entry: any): undefined | Timestamp {
+ if (type === TimestampType.REAL) {
+ return new RealTimestamp(BigInt(entry.timestamp.unixNanos));
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(index: number, timestampType: TimestampType, entry: Event): Event {
+ return entry;
+ }
+
+ private static readonly MAGIC_NUMBER: number[] = Array.from(new TextEncoder().encode('EventLog'));
+}
+
+export {ParserEventLog};
diff --git a/tools/winscope/src/parsers/parser_eventlog_test.ts b/tools/winscope/src/parsers/parser_eventlog_test.ts
new file mode 100644
index 0000000..bf89d54
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_eventlog_test.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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 {assertDefined} from 'common/assert_utils';
+import {UnitTestUtils} from 'test/unit/utils';
+import {CujEvent} from 'trace/flickerlib/common';
+import {Parser} from 'trace/parser';
+import {RealTimestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserEventLog', () => {
+ let parser: Parser<Event>;
+
+ beforeAll(async () => {
+ parser = assertDefined(
+ await UnitTestUtils.getParser('traces/eventlog.winscope')
+ ) as Parser<Event>;
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.EVENT_LOG);
+ });
+
+ it('has expected timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
+
+ expect(timestamps.length).toEqual(184);
+
+ const expected = [
+ new RealTimestamp(1681207047981157120n),
+ new RealTimestamp(1681207047991161088n),
+ new RealTimestamp(1681207047991310592n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it("doesn't provide elapsed timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(undefined);
+ });
+
+ it('contains parsed jank CUJ events', () => {
+ const entry = parser.getEntry(18, TimestampType.REAL);
+ expect(entry instanceof CujEvent).toBeTrue();
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_factory.ts b/tools/winscope/src/parsers/parser_factory.ts
new file mode 100644
index 0000000..95ebb8d
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_factory.ts
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 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 {FunctionUtils, OnProgressUpdateType} from 'common/function_utils';
+import {Parser} from 'trace/parser';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {ParserAccessibility} from './parser_accessibility';
+import {ParserEventLog} from './parser_eventlog';
+import {ParserInputMethodClients} from './parser_input_method_clients';
+import {ParserInputMethodManagerService} from './parser_input_method_manager_service';
+import {ParserInputMethodService} from './parser_input_method_service';
+import {ParserProtoLog} from './parser_protolog';
+import {ParserScreenRecording} from './parser_screen_recording';
+import {ParserScreenRecordingLegacy} from './parser_screen_recording_legacy';
+import {ParserSurfaceFlinger} from './parser_surface_flinger';
+import {ParserTransactions} from './parser_transactions';
+import {ParserTransitionsShell} from './parser_transitions_shell';
+import {ParserTransitionsWm} from './parser_transitions_wm';
+import {ParserWindowManager} from './parser_window_manager';
+import {ParserWindowManagerDump} from './parser_window_manager_dump';
+
+export class ParserFactory {
+ static readonly PARSERS = [
+ ParserAccessibility,
+ ParserInputMethodClients,
+ ParserInputMethodManagerService,
+ ParserInputMethodService,
+ ParserProtoLog,
+ ParserScreenRecording,
+ ParserScreenRecordingLegacy,
+ ParserSurfaceFlinger,
+ ParserTransactions,
+ ParserWindowManager,
+ ParserWindowManagerDump,
+ ParserEventLog,
+ ParserTransitionsWm,
+ ParserTransitionsShell,
+ ];
+
+ private parsers = new Map<TraceType, Parser<object>>();
+
+ async createParsers(
+ traceFiles: TraceFile[],
+ onProgressUpdate: OnProgressUpdateType = FunctionUtils.DO_NOTHING
+ ): Promise<[Array<{file: TraceFile; parser: Parser<object>}>, ParserError[]]> {
+ const errors: ParserError[] = [];
+
+ const parsers = new Array<{file: TraceFile; parser: Parser<object>}>();
+
+ if (traceFiles.length === 0) {
+ errors.push(new ParserError(ParserErrorType.NO_INPUT_FILES));
+ }
+
+ for (const [index, traceFile] of traceFiles.entries()) {
+ let hasFoundParser = false;
+
+ for (const ParserType of ParserFactory.PARSERS) {
+ try {
+ const parser = new ParserType(traceFile);
+ await parser.parse();
+ hasFoundParser = true;
+ if (this.shouldUseParser(parser, errors)) {
+ this.parsers.set(parser.getTraceType(), parser);
+ parsers.push({file: traceFile, parser});
+ }
+ break;
+ } catch (error) {
+ // skip current parser
+ }
+ }
+
+ if (!hasFoundParser) {
+ console.log(`Failed to load trace ${traceFile.file.name}`);
+ errors.push(new ParserError(ParserErrorType.UNSUPPORTED_FORMAT, traceFile.getDescriptor()));
+ }
+
+ onProgressUpdate((100 * (index + 1)) / traceFiles.length);
+ }
+
+ return [parsers, errors];
+ }
+
+ private shouldUseParser(newParser: Parser<object>, errors: ParserError[]): boolean {
+ const oldParser = this.parsers.get(newParser.getTraceType());
+ if (!oldParser) {
+ console.log(
+ `Loaded trace ${newParser
+ .getDescriptors()
+ .join()} (trace type: ${newParser.getTraceType()})`
+ );
+ return true;
+ }
+
+ if (newParser.getLengthEntries() > oldParser.getLengthEntries()) {
+ console.log(
+ `Loaded trace ${newParser
+ .getDescriptors()
+ .join()} (trace type: ${newParser.getTraceType()}).` +
+ ` Replace trace ${oldParser.getDescriptors().join()}`
+ );
+ errors.push(
+ new ParserError(
+ ParserErrorType.OVERRIDE,
+ oldParser.getDescriptors().join(),
+ oldParser.getTraceType()
+ )
+ );
+ return true;
+ }
+
+ console.log(
+ `Skipping trace ${newParser
+ .getDescriptors()
+ .join()} (trace type: ${newParser.getTraceType()}).` +
+ ` Keep trace ${oldParser.getDescriptors().join()}`
+ );
+ errors.push(
+ new ParserError(
+ ParserErrorType.OVERRIDE,
+ newParser.getDescriptors().join(),
+ newParser.getTraceType()
+ )
+ );
+ return false;
+ }
+}
+
+export enum ParserErrorType {
+ NO_INPUT_FILES,
+ UNSUPPORTED_FORMAT,
+ OVERRIDE,
+}
+
+export class ParserError {
+ constructor(
+ public type: ParserErrorType,
+ public trace: string | undefined = undefined,
+ public traceType: TraceType | undefined = undefined
+ ) {}
+}
diff --git a/tools/winscope/src/parsers/parser_input_method_clients.ts b/tools/winscope/src/parsers/parser_input_method_clients.ts
new file mode 100644
index 0000000..796df79
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_input_method_clients.ts
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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 {TimeUtils} from 'common/time_utils';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TraceType} from 'trace/trace_type';
+import {ImeUtils} from 'viewers/common/ime_utils';
+import {AbstractParser} from './abstract_parser';
+import {InputMethodClientsTraceFileProto} from './proto_types';
+
+class ParserInputMethodClients extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ getTraceType(): TraceType {
+ return TraceType.INPUT_METHOD_CLIENTS;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserInputMethodClients.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decoded = InputMethodClientsTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
+ } else {
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ return (InputMethodClientsTraceFileProto.decode(buffer) as any).entry;
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: TraceTreeNode
+ ): TraceTreeNode {
+ if (entryProto.elapsedRealtimeNanos === undefined) {
+ throw Error('Missing elapsedRealtimeNanos on entry');
+ }
+
+ let clockTimeNanos: bigint | undefined = undefined;
+ if (
+ this.realToElapsedTimeOffsetNs !== undefined &&
+ entryProto.elapsedRealtimeNanos !== undefined
+ ) {
+ clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs;
+ }
+
+ const timestamp = Timestamp.from(
+ timestampType,
+ BigInt(entryProto.elapsedRealtimeNanos),
+ this.realToElapsedTimeOffsetNs
+ );
+
+ return {
+ name: TimeUtils.format(timestamp) + ' - ' + entryProto.where,
+ kind: 'InputMethodClient entry',
+ children: [
+ {
+ obj: ImeUtils.transformInputConnectionCall(entryProto.client),
+ kind: 'Client',
+ name: entryProto.client?.viewRootImpl?.view ?? '',
+ children: [],
+ stableId: 'client',
+ id: 'client',
+ },
+ ],
+ obj: entryProto,
+ stableId: 'entry',
+ id: 'entry',
+ elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
+ clockTimeNanos,
+ };
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE
+}
+
+export {ParserInputMethodClients};
diff --git a/tools/winscope/src/parsers/parser_input_method_clients_test.ts b/tools/winscope/src/parsers/parser_input_method_clients_test.ts
new file mode 100644
index 0000000..24fbb40
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_input_method_clients_test.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserInputMethodlClients', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/InputMethodClients.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)!.length).toEqual(13);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 15613638434n),
+ new Timestamp(TimestampType.ELAPSED, 15647516364n),
+ new Timestamp(TimestampType.ELAPSED, 15677650967n),
+ ];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1659107090215405395n),
+ new Timestamp(TimestampType.REAL, 1659107090249283325n),
+ new Timestamp(TimestampType.REAL, 1659107090279417928n),
+ ];
+ expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ expect(BigInt(parser.getEntry(1, TimestampType.REAL)!.elapsedRealtimeNanos)).toEqual(
+ 15647516364n
+ );
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/InputMethodClients.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)![0]).toEqual(
+ new Timestamp(TimestampType.ELAPSED, 1149083651642n)
+ );
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toBeUndefined();
+ });
+
+ it('retrieves trace entry from elapsed timestamp', () => {
+ expect(BigInt(parser.getEntry(0, TimestampType.ELAPSED)!.elapsedRealtimeNanos)).toEqual(
+ 1149083651642n
+ );
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_input_method_manager_service.ts b/tools/winscope/src/parsers/parser_input_method_manager_service.ts
new file mode 100644
index 0000000..a256f54
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_input_method_manager_service.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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 {TimeUtils} from 'common/time_utils';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {InputMethodManagerServiceTraceFileProto} from './proto_types';
+
+class ParserInputMethodManagerService extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ getTraceType(): TraceType {
+ return TraceType.INPUT_METHOD_MANAGER_SERVICE;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserInputMethodManagerService.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decoded = InputMethodManagerServiceTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
+ } else {
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decoded.entry;
+ }
+
+ protected override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos)
+ );
+ }
+ return undefined;
+ }
+
+ protected override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: TraceTreeNode
+ ): TraceTreeNode {
+ if (entryProto.elapsedRealtimeNanos === undefined) {
+ throw Error('Missing elapsedRealtimeNanos on entry');
+ }
+
+ let clockTimeNanos: bigint | undefined = undefined;
+ if (
+ this.realToElapsedTimeOffsetNs !== undefined &&
+ entryProto.elapsedRealtimeNanos !== undefined
+ ) {
+ clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs;
+ }
+
+ const timestamp = Timestamp.from(
+ timestampType,
+ BigInt(entryProto.elapsedRealtimeNanos),
+ this.realToElapsedTimeOffsetNs
+ );
+
+ return {
+ name: TimeUtils.format(timestamp) + ' - ' + entryProto.where,
+ kind: 'InputMethodManagerService entry',
+ children: [
+ {
+ obj: entryProto.inputMethodManagerService,
+ kind: 'InputMethodManagerService',
+ name: '',
+ children: [],
+ stableId: 'managerservice',
+ id: 'managerservice',
+ },
+ ],
+ obj: entryProto,
+ stableId: 'entry',
+ id: 'entry',
+ elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
+ clockTimeNanos,
+ };
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
+}
+
+export {ParserInputMethodManagerService};
diff --git a/tools/winscope/src/parsers/parser_input_method_manager_service_test.ts b/tools/winscope/src/parsers/parser_input_method_manager_service_test.ts
new file mode 100644
index 0000000..a6d3160
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_input_method_manager_service_test.ts
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserInputMethodManagerService', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/InputMethodManagerService.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual([
+ new Timestamp(TimestampType.ELAPSED, 15963782518n),
+ ]);
+ });
+
+ it('provides real timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual([
+ new Timestamp(TimestampType.REAL, 1659107090565549479n),
+ ]);
+ });
+
+ it('retrieves trace entry', () => {
+ expect(BigInt(parser.getEntry(0, TimestampType.REAL)!.elapsedRealtimeNanos)).toEqual(
+ 15963782518n
+ );
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_timestamp/InputMethodManagerService.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)![0]).toEqual(
+ new Timestamp(TimestampType.ELAPSED, 1149226290110n)
+ );
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+
+ it('retrieves trace entry from elapsed timestamp', () => {
+ expect(BigInt(parser.getEntry(0, TimestampType.ELAPSED)!.elapsedRealtimeNanos)).toEqual(
+ 1149226290110n
+ );
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_input_method_service.ts b/tools/winscope/src/parsers/parser_input_method_service.ts
new file mode 100644
index 0000000..5e31ea7
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_input_method_service.ts
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 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 {TimeUtils} from 'common/time_utils';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TraceType} from 'trace/trace_type';
+import {ImeUtils} from 'viewers/common/ime_utils';
+import {AbstractParser} from './abstract_parser';
+import {InputMethodServiceTraceFileProto} from './proto_types';
+
+class ParserInputMethodService extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ getTraceType(): TraceType {
+ return TraceType.INPUT_METHOD_SERVICE;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserInputMethodService.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decoded = InputMethodServiceTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
+ } else {
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decoded.entry;
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos)
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: TraceTreeNode
+ ): TraceTreeNode {
+ if (entryProto.elapsedRealtimeNanos === undefined) {
+ throw Error('Missing elapsedRealtimeNanos on entry');
+ }
+
+ let clockTimeNanos: bigint | undefined = undefined;
+ if (
+ this.realToElapsedTimeOffsetNs !== undefined &&
+ entryProto.elapsedRealtimeNanos !== undefined
+ ) {
+ clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs;
+ }
+
+ const timestamp = Timestamp.from(
+ timestampType,
+ BigInt(entryProto.elapsedRealtimeNanos),
+ this.realToElapsedTimeOffsetNs
+ );
+
+ return {
+ name: TimeUtils.format(timestamp) + ' - ' + entryProto.where,
+ kind: 'InputMethodService entry',
+ children: [
+ {
+ obj: ImeUtils.transformInputConnectionCall(entryProto.inputMethodService),
+ kind: 'InputMethodService',
+ name: '',
+ children: [],
+ stableId: 'service',
+ id: 'service',
+ },
+ ],
+ obj: entryProto,
+ stableId: 'entry',
+ id: 'entry',
+ elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
+ clockTimeNanos,
+ };
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE
+}
+
+export {ParserInputMethodService};
diff --git a/tools/winscope/src/parsers/parser_input_method_service_test.ts b/tools/winscope/src/parsers/parser_input_method_service_test.ts
new file mode 100644
index 0000000..a53a3c4
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_input_method_service_test.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserInputMethodService', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/InputMethodService.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const expected = [new Timestamp(TimestampType.ELAPSED, 16578752896n)];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [new Timestamp(TimestampType.REAL, 1659107091180519857n)];
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ expect(BigInt(parser.getEntry(0, TimestampType.REAL)!.elapsedRealtimeNanos)).toEqual(
+ 16578752896n
+ );
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<any>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/InputMethodService.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)![0]).toEqual(
+ new Timestamp(TimestampType.ELAPSED, 1149230019887n)
+ );
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+
+ it('retrieves trace entry', () => {
+ expect(BigInt(parser.getEntry(0, TimestampType.ELAPSED)!.elapsedRealtimeNanos)).toEqual(
+ 1149230019887n
+ );
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_protolog.ts b/tools/winscope/src/parsers/parser_protolog.ts
new file mode 100644
index 0000000..4c0c087
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_protolog.ts
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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 {FormattedLogMessage, LogMessage, UnformattedLogMessage} from 'trace/protolog';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import configJson from '../../../../../frameworks/base/data/etc/services.core.protolog.json';
+import {AbstractParser} from './abstract_parser';
+import {ProtoLogFileProto} from './proto_types';
+
+class ParserProtoLog extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.PROTO_LOG;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserProtoLog.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const fileProto: any = ProtoLogFileProto.decode(buffer);
+
+ if (fileProto.version !== ParserProtoLog.PROTOLOG_VERSION) {
+ const message = 'Unsupported ProtoLog trace version';
+ console.log(message);
+ throw new TypeError(message);
+ }
+
+ if (configJson.version !== ParserProtoLog.PROTOLOG_VERSION) {
+ const message = 'Unsupported ProtoLog JSON config version';
+ console.log(message);
+ throw new TypeError(message);
+ }
+
+ this.realToElapsedTimeOffsetNs = BigInt(fileProto.realTimeToElapsedTimeOffsetMillis) * 1000000n;
+
+ fileProto.log.sort((a: any, b: any) => {
+ return Number(a.elapsedRealtimeNanos) - Number(b.elapsedRealtimeNanos);
+ });
+
+ return fileProto.log;
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ }
+ if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: object
+ ): LogMessage {
+ const message = (configJson as any).messages[(entryProto as any).messageHash];
+ if (!message) {
+ return new FormattedLogMessage(entryProto, timestampType, this.realToElapsedTimeOffsetNs);
+ }
+
+ try {
+ return new UnformattedLogMessage(
+ entryProto,
+ timestampType,
+ this.realToElapsedTimeOffsetNs,
+ message
+ );
+ } catch (error) {
+ if (error instanceof FormatStringMismatchError) {
+ return new FormattedLogMessage(entryProto, timestampType, this.realToElapsedTimeOffsetNs);
+ }
+ throw error;
+ }
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint = undefined;
+ private static readonly MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
+ private static readonly PROTOLOG_VERSION = '1.0.0';
+}
+
+class FormatStringMismatchError extends Error {
+ constructor(message: string) {
+ super(message);
+ }
+}
+
+export {ParserProtoLog};
diff --git a/tools/winscope/src/parsers/parser_protolog_test.ts b/tools/winscope/src/parsers/parser_protolog_test.ts
new file mode 100644
index 0000000..0a3cc78
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_protolog_test.ts
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {LogMessage} from 'trace/protolog';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserProtoLog', () => {
+ let parser: Parser<LogMessage>;
+
+ const expectedFirstLogMessageElapsed = {
+ text: 'InsetsSource updateVisibility for ITYPE_IME, serverVisible: false clientVisible: false',
+ time: '14m10s746ms266486ns',
+ tag: 'WindowManager',
+ level: 'DEBUG',
+ at: 'com/android/server/wm/InsetsSourceProvider.java',
+ timestamp: 850746266486n,
+ };
+
+ const expectedFirstLogMessageReal = {
+ text: 'InsetsSource updateVisibility for ITYPE_IME, serverVisible: false clientVisible: false',
+ time: '2022-06-20T12:12:05.377266486',
+ tag: 'WindowManager',
+ level: 'DEBUG',
+ at: 'com/android/server/wm/InsetsSourceProvider.java',
+ timestamp: 1655727125377266486n,
+ };
+
+ beforeAll(async () => {
+ parser = (await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/ProtoLog.pb'
+ )) as Parser<LogMessage>;
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.PROTO_LOG);
+ });
+
+ it('has expected length', () => {
+ expect(parser.getLengthEntries()).toEqual(50);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+ expect(timestamps.length).toEqual(50);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 850746266486n),
+ new Timestamp(TimestampType.ELAPSED, 850746336718n),
+ new Timestamp(TimestampType.ELAPSED, 850746350430n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+ expect(timestamps.length).toEqual(50);
+
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1655727125377266486n),
+ new Timestamp(TimestampType.REAL, 1655727125377336718n),
+ new Timestamp(TimestampType.REAL, 1655727125377350430n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('reconstructs human-readable log message (ELAPSED time)', () => {
+ const message = parser.getEntry(0, TimestampType.ELAPSED);
+
+ expect(Object.assign({}, message)).toEqual(expectedFirstLogMessageElapsed);
+ expect(message).toBeInstanceOf(LogMessage);
+ });
+
+ it('reconstructs human-readable log message (REAL time)', () => {
+ const message = parser.getEntry(0, TimestampType.REAL)!;
+
+ expect(Object.assign({}, message)).toEqual(expectedFirstLogMessageReal);
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_screen_recording.ts b/tools/winscope/src/parsers/parser_screen_recording.ts
new file mode 100644
index 0000000..0550da5
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_screen_recording.ts
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 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 {ArrayUtils} from 'common/array_utils';
+import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+import {ScreenRecordingUtils} from 'trace/screen_recording_utils';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+
+class ScreenRecordingMetadataEntry {
+ constructor(public timestampElapsedNs: bigint, public timestampRealtimeNs: bigint) {}
+}
+
+class ParserScreenRecording extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.SCREEN_RECORDING;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserScreenRecording.MPEG4_MAGIC_NMBER;
+ }
+
+ override decodeTrace(videoData: Uint8Array): ScreenRecordingMetadataEntry[] {
+ const posVersion = this.searchMagicString(videoData);
+ const [posTimeOffset, metadataVersion] = this.parseMetadataVersion(videoData, posVersion);
+
+ if (metadataVersion !== 1 && metadataVersion !== 2) {
+ throw TypeError(`Metadata version "${metadataVersion}" not supported`);
+ }
+
+ if (metadataVersion === 1) {
+ // UI traces contain "elapsed" timestamps (SYSTEM_TIME_BOOTTIME), whereas
+ // metadata Version 1 contains SYSTEM_TIME_MONOTONIC timestamps.
+ //
+ // Here we are pretending that metadata Version 1 contains "elapsed"
+ // timestamps as well, in order to synchronize with the other traces.
+ //
+ // If no device suspensions are involved, SYSTEM_TIME_MONOTONIC should
+ // indeed correspond to SYSTEM_TIME_BOOTTIME and things will work as
+ // expected.
+ console.warn(`Screen recording may not be synchronized with the
+ other traces. Metadata contains monotonic time instead of elapsed.`);
+ }
+
+ const [posCount, timeOffsetNs] = this.parseRealToElapsedTimeOffsetNs(videoData, posTimeOffset);
+ const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
+ const timestampsElapsedNs = this.parseTimestampsElapsedNs(videoData, posTimestamps, count);
+
+ return timestampsElapsedNs.map((timestampElapsedNs: bigint) => {
+ return new ScreenRecordingMetadataEntry(
+ timestampElapsedNs,
+ timestampElapsedNs + timeOffsetNs
+ );
+ });
+ }
+
+ override getTimestamp(
+ type: TimestampType,
+ decodedEntry: ScreenRecordingMetadataEntry
+ ): undefined | Timestamp {
+ if (type !== TimestampType.ELAPSED && type !== TimestampType.REAL) {
+ return undefined;
+ }
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, decodedEntry.timestampElapsedNs);
+ } else if (type === TimestampType.REAL) {
+ return new Timestamp(type, decodedEntry.timestampRealtimeNs);
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entry: ScreenRecordingMetadataEntry
+ ): ScreenRecordingTraceEntry {
+ const initialTimestamp = this.getTimestamps(TimestampType.ELAPSED)![0];
+ const currentTimestamp = new Timestamp(TimestampType.ELAPSED, entry.timestampElapsedNs);
+ const videoTimeSeconds = ScreenRecordingUtils.timestampToVideoTimeSeconds(
+ initialTimestamp,
+ currentTimestamp
+ );
+ const videoData = this.traceFile.file;
+ return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
+ }
+
+ private searchMagicString(videoData: Uint8Array): number {
+ let pos = ArrayUtils.searchSubarray(
+ videoData,
+ ParserScreenRecording.WINSCOPE_META_MAGIC_STRING
+ );
+ if (pos === undefined) {
+ throw new TypeError("video data doesn't contain winscope magic string");
+ }
+ pos += ParserScreenRecording.WINSCOPE_META_MAGIC_STRING.length;
+ return pos;
+ }
+
+ private parseMetadataVersion(videoData: Uint8Array, pos: number): [number, number] {
+ if (pos + 4 > videoData.length) {
+ throw new TypeError('Failed to parse metadata version. Video data is too short.');
+ }
+ const version = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos + 4));
+ pos += 4;
+ return [pos, version];
+ }
+
+ private parseRealToElapsedTimeOffsetNs(videoData: Uint8Array, pos: number): [number, bigint] {
+ if (pos + 8 > videoData.length) {
+ throw new TypeError(
+ 'Failed to parse realtime-to-elapsed time offset. Video data is too short.'
+ );
+ }
+ const offset = ArrayUtils.toIntLittleEndian(videoData, pos, pos + 8);
+ pos += 8;
+ return [pos, offset];
+ }
+
+ private parseFramesCount(videoData: Uint8Array, pos: number): [number, number] {
+ if (pos + 4 > videoData.length) {
+ throw new TypeError('Failed to parse frames count. Video data is too short.');
+ }
+ const count = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos + 4));
+ pos += 4;
+ return [pos, count];
+ }
+
+ private parseTimestampsElapsedNs(
+ videoData: Uint8Array,
+ pos: number,
+ count: number
+ ): Array<bigint> {
+ if (pos + count * 8 > videoData.length) {
+ throw new TypeError('Failed to parse timestamps. Video data is too short.');
+ }
+ const timestamps: Array<bigint> = [];
+ for (let i = 0; i < count; ++i) {
+ const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos + 8);
+ pos += 8;
+ timestamps.push(timestamp);
+ }
+ return timestamps;
+ }
+
+ private static readonly MPEG4_MAGIC_NMBER = [
+ 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32,
+ ]; // ....ftypmp42
+ private static readonly WINSCOPE_META_MAGIC_STRING = [
+ 0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x32, 0x23,
+ ]; // #VV1NSC0PET1ME2#
+}
+
+export {ParserScreenRecording};
diff --git a/tools/winscope/src/parsers/parser_screen_recording_legacy.ts b/tools/winscope/src/parsers/parser_screen_recording_legacy.ts
new file mode 100644
index 0000000..1aac6ca
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_screen_recording_legacy.ts
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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 {ArrayUtils} from 'common/array_utils';
+import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+
+class ParserScreenRecordingLegacy extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.SCREEN_RECORDING;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserScreenRecordingLegacy.MPEG4_MAGIC_NMBER;
+ }
+
+ override decodeTrace(videoData: Uint8Array): Timestamp[] {
+ const posCount = this.searchMagicString(videoData);
+ const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
+ return this.parseTimestamps(videoData, posTimestamps, count);
+ }
+
+ override getTimestamp(type: TimestampType, decodedEntry: Timestamp): undefined | Timestamp {
+ if (type !== TimestampType.ELAPSED) {
+ return undefined;
+ }
+ return decodedEntry;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entry: Timestamp
+ ): ScreenRecordingTraceEntry {
+ const currentTimestamp = entry;
+ const initialTimestamp = this.getTimestamps(TimestampType.ELAPSED)![0];
+ const videoTimeSeconds =
+ Number(currentTimestamp.getValueNs() - initialTimestamp.getValueNs()) / 1000000000 +
+ ParserScreenRecordingLegacy.EPSILON;
+ const videoData = this.traceFile.file;
+ return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
+ }
+
+ private searchMagicString(videoData: Uint8Array): number {
+ let pos = ArrayUtils.searchSubarray(
+ videoData,
+ ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING
+ );
+ if (pos === undefined) {
+ throw new TypeError("video data doesn't contain winscope magic string");
+ }
+ pos += ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING.length;
+ return pos;
+ }
+
+ private parseFramesCount(videoData: Uint8Array, pos: number): [number, number] {
+ if (pos + 4 > videoData.length) {
+ throw new TypeError('Failed to parse frames count. Video data is too short.');
+ }
+ const framesCount = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos + 4));
+ pos += 4;
+ return [pos, framesCount];
+ }
+
+ private parseTimestamps(videoData: Uint8Array, pos: number, count: number): Timestamp[] {
+ if (pos + count * 8 > videoData.length) {
+ throw new TypeError('Failed to parse timestamps. Video data is too short.');
+ }
+ const timestamps: Timestamp[] = [];
+ for (let i = 0; i < count; ++i) {
+ const value = ArrayUtils.toUintLittleEndian(videoData, pos, pos + 8) * 1000n;
+ pos += 8;
+ timestamps.push(new Timestamp(TimestampType.ELAPSED, value));
+ }
+ return timestamps;
+ }
+
+ private static readonly MPEG4_MAGIC_NMBER = [
+ 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32,
+ ]; // ....ftypmp42
+ private static readonly WINSCOPE_META_MAGIC_STRING = [
+ 0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23,
+ ]; // #VV1NSC0PET1ME!#
+ private static readonly EPSILON = 0.00001;
+}
+
+export {ParserScreenRecordingLegacy};
diff --git a/tools/winscope/src/parsers/parser_screen_recording_legacy_test.ts b/tools/winscope/src/parsers/parser_screen_recording_legacy_test.ts
new file mode 100644
index 0000000..142983b
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_screen_recording_legacy_test.ts
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserScreenRecordingLegacy', () => {
+ let parser: Parser<ScreenRecordingTraceEntry>;
+
+ beforeAll(async () => {
+ parser = (await UnitTestUtils.getParser(
+ 'traces/elapsed_timestamp/screen_recording.mp4'
+ )) as Parser<ScreenRecordingTraceEntry>;
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SCREEN_RECORDING);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(85);
+
+ let expected = [
+ new Timestamp(TimestampType.ELAPSED, 19446131807000n),
+ new Timestamp(TimestampType.ELAPSED, 19446158500000n),
+ new Timestamp(TimestampType.ELAPSED, 19446167117000n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+
+ expected = [
+ new Timestamp(TimestampType.ELAPSED, 19448470076000n),
+ new Timestamp(TimestampType.ELAPSED, 19448487525000n),
+ new Timestamp(TimestampType.ELAPSED, 19448501007000n),
+ ];
+ expect(timestamps.slice(timestamps.length - 3, timestamps.length)).toEqual(expected);
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+
+ it('retrieves trace entry', () => {
+ {
+ const entry = parser.getEntry(0, TimestampType.ELAPSED);
+ expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
+ expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
+ }
+ {
+ const entry = parser.getEntry(parser.getLengthEntries() - 1, TimestampType.ELAPSED);
+ expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
+ expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001);
+ }
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_screen_recording_test.ts b/tools/winscope/src/parsers/parser_screen_recording_test.ts
new file mode 100644
index 0000000..4286ab2
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_screen_recording_test.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserScreenRecording', () => {
+ let parser: Parser<ScreenRecordingTraceEntry>;
+
+ beforeAll(async () => {
+ parser = (await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4'
+ )) as Parser<ScreenRecordingTraceEntry>;
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SCREEN_RECORDING);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(123);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 211827840430n),
+ new Timestamp(TimestampType.ELAPSED, 211842401430n),
+ new Timestamp(TimestampType.ELAPSED, 211862172430n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+
+ expect(timestamps.length).toEqual(123);
+
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1666361048792787045n),
+ new Timestamp(TimestampType.REAL, 1666361048807348045n),
+ new Timestamp(TimestampType.REAL, 1666361048827119045n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ {
+ const entry = parser.getEntry(0, TimestampType.REAL);
+ expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
+ expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
+ }
+ {
+ const entry = parser.getEntry(parser.getLengthEntries() - 1, TimestampType.REAL);
+ expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
+ expect(Number(entry.videoTimeSeconds)).toBeCloseTo(1.371077, 0.001);
+ }
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_surface_flinger.ts b/tools/winscope/src/parsers/parser_surface_flinger.ts
new file mode 100644
index 0000000..5cb0cce
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_surface_flinger.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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 {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {LayersTraceFileProto} from './proto_types';
+
+class ParserSurfaceFlinger extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.SURFACE_FLINGER;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserSurfaceFlinger.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decoded = LayersTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
+ } else {
+ console.warn('Missing realToElapsedTimeOffsetNanos property on SF trace proto');
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decoded.entry;
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ const isDump = !Object.prototype.hasOwnProperty.call(entryProto, 'elapsedRealtimeNanos');
+ if (type === TimestampType.ELAPSED) {
+ return isDump
+ ? new Timestamp(type, 0n)
+ : new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return isDump
+ ? new Timestamp(type, 0n)
+ : new Timestamp(
+ type,
+ this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos)
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: any
+ ): LayerTraceEntry {
+ return LayerTraceEntry.fromProto(
+ entryProto.layers.layers,
+ entryProto.displays,
+ BigInt(entryProto.elapsedRealtimeNanos.toString()),
+ entryProto.vsyncId,
+ entryProto.hwcBlob,
+ entryProto.where,
+ this.realToElapsedTimeOffsetNs,
+ timestampType === TimestampType.ELAPSED /*useElapsedTime*/,
+ entryProto.excludesCompositionState ?? false
+ );
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
+}
+
+export {ParserSurfaceFlinger};
diff --git a/tools/winscope/src/parsers/parser_surface_flinger_dump_test.ts b/tools/winscope/src/parsers/parser_surface_flinger_dump_test.ts
new file mode 100644
index 0000000..eaa28b3
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_surface_flinger_dump_test.ts
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserSurfaceFlingerDump', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<LayerTraceEntry>;
+ const DUMP_REAL_TIME = 1659176624505188647n;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
+ });
+
+ it('provides elapsed timestamp', () => {
+ const expected = [new Timestamp(TimestampType.ELAPSED, 0n)];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ });
+
+ it('provides real timestamp (always zero)', () => {
+ const expected = [new Timestamp(TimestampType.REAL, 0n)];
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ const entry = parser.getEntry(0, TimestampType.ELAPSED);
+ expect(entry).toBeInstanceOf(LayerTraceEntry);
+ expect(BigInt(entry.timestamp.systemUptimeNanos.toString())).toEqual(0n);
+ expect(BigInt(entry.timestamp.unixNanos.toString())).toEqual(DUMP_REAL_TIME);
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<LayerTraceEntry>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/dump_SurfaceFlinger.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
+ });
+
+ it('provides elapsed timestamp (always zero)', () => {
+ const expected = [new Timestamp(TimestampType.ELAPSED, 0n)];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ });
+
+ it("doesn't provide real timestamp", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_surface_flinger_test.ts b/tools/winscope/src/parsers/parser_surface_flinger_test.ts
new file mode 100644
index 0000000..a643c19
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_surface_flinger_test.ts
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Layer} from 'trace/flickerlib/layers/Layer';
+import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserSurfaceFlinger', () => {
+ it('decodes layer state flags', async () => {
+ const parser = (await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb'
+ )) as Parser<LayerTraceEntry>;
+ const entry = parser.getEntry(0, TimestampType.REAL);
+
+ {
+ const layer = entry.flattenedLayers.find((layer: Layer) => layer.id === 27);
+ expect(layer.name).toEqual('Leaf:24:25#27');
+ expect(layer.flags).toEqual(0x0);
+ expect(layer.verboseFlags).toEqual('');
+ }
+ {
+ const layer = entry.flattenedLayers.find((layer: Layer) => layer.id === 48);
+ expect(layer.name).toEqual('Task=4#48');
+ expect(layer.flags).toEqual(0x1);
+ expect(layer.verboseFlags).toEqual('HIDDEN (0x1)');
+ }
+ {
+ const layer = entry.flattenedLayers.find((layer: Layer) => layer.id === 77);
+ expect(layer.name).toEqual('Wallpaper BBQ wrapper#77');
+ expect(layer.flags).toEqual(0x100);
+ expect(layer.verboseFlags).toEqual('ENABLE_BACKPRESSURE (0x100)');
+ }
+ });
+
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<LayerTraceEntry>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 14500282843n),
+ new Timestamp(TimestampType.ELAPSED, 14631249355n),
+ new Timestamp(TimestampType.ELAPSED, 15403446377n),
+ ];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1659107089102062832n),
+ new Timestamp(TimestampType.REAL, 1659107089233029344n),
+ new Timestamp(TimestampType.REAL, 1659107090005226366n),
+ ];
+ expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('formats entry timestamps', () => {
+ const entry = parser.getEntry(1, TimestampType.REAL);
+ expect(entry.name).toEqual('2022-07-29T15:04:49.233029376');
+ expect(BigInt(entry.timestamp.systemUptimeNanos.toString())).toEqual(14631249355n);
+ expect(BigInt(entry.timestamp.unixNanos.toString())).toEqual(1659107089233029344n);
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<LayerTraceEntry>;
+
+ beforeAll(async () => {
+ parser = (await UnitTestUtils.getParser(
+ 'traces/elapsed_timestamp/SurfaceFlinger.pb'
+ )) as Parser<LayerTraceEntry>;
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
+ });
+
+ it('provides elapsed timestamps', () => {
+ expect(parser.getTimestamps(TimestampType.ELAPSED)![0]).toEqual(
+ new Timestamp(TimestampType.ELAPSED, 850335483446n)
+ );
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+
+ it('formats entry timestamps', () => {
+ const entry = parser.getEntry(0, TimestampType.ELAPSED);
+ expect(entry.name).toEqual('14m10s335ms483446ns');
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_transactions.ts b/tools/winscope/src/parsers/parser_transactions.ts
new file mode 100644
index 0000000..14fc2a9
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_transactions.ts
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 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 {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {TransactionsTraceFileProto} from './proto_types';
+
+class ParserTransactions extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.TRANSACTIONS;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserTransactions.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decodedProto = TransactionsTraceFileProto.decode(buffer) as any;
+ this.decodeWhatFields(decodedProto);
+
+ if (Object.prototype.hasOwnProperty.call(decodedProto, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos);
+ } else {
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decodedProto.entry;
+ }
+
+ private decodeWhatFields(decodedProto: any) {
+ const decodeBitset32 = (bitset: number, EnumProto: any) => {
+ return Object.keys(EnumProto).filter((key) => {
+ const value = EnumProto[key];
+ return (bitset & value) !== 0;
+ });
+ };
+
+ const concatBitsetTokens = (tokens: string[]) => {
+ if (tokens.length === 0) {
+ return '0';
+ }
+ return tokens.join(' | ');
+ };
+
+ const LayerStateChangesLsbEnum = (TransactionsTraceFileProto?.parent as any).LayerState
+ .ChangesLsb;
+ const LayerStateChangesMsbEnum = (TransactionsTraceFileProto?.parent as any).LayerState
+ .ChangesMsb;
+ const DisplayStateChangesEnum = (TransactionsTraceFileProto?.parent as any).DisplayState
+ .Changes;
+
+ decodedProto.entry.forEach((transactionTraceEntry: any) => {
+ transactionTraceEntry.transactions.forEach((transactionState: any) => {
+ transactionState.layerChanges.forEach((layerState: any) => {
+ layerState.what = concatBitsetTokens(
+ decodeBitset32(layerState.what.low, LayerStateChangesLsbEnum).concat(
+ decodeBitset32(layerState.what.high, LayerStateChangesMsbEnum)
+ )
+ );
+ });
+
+ transactionState.displayChanges.forEach((displayState: any) => {
+ displayState.what = concatBitsetTokens(
+ decodeBitset32(displayState.what, DisplayStateChangesEnum)
+ );
+ });
+ });
+
+ transactionTraceEntry.addedDisplays.forEach((displayState: any) => {
+ displayState.what = concatBitsetTokens(
+ decodeBitset32(displayState.what, DisplayStateChangesEnum)
+ );
+ });
+ });
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos)
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: object
+ ): object {
+ return entryProto;
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x54, 0x4e, 0x58, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TNXTRACE
+}
+
+export {ParserTransactions};
diff --git a/tools/winscope/src/parsers/parser_transactions_test.ts b/tools/winscope/src/parsers/parser_transactions_test.ts
new file mode 100644
index 0000000..e59d620
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_transactions_test.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserTransactions', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<object>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/Transactions.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(712);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 2450981445n),
+ new Timestamp(TimestampType.ELAPSED, 2517952515n),
+ new Timestamp(TimestampType.ELAPSED, 4021151449n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+
+ expect(timestamps.length).toEqual(712);
+
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1659507541051480997n),
+ new Timestamp(TimestampType.REAL, 1659507541118452067n),
+ new Timestamp(TimestampType.REAL, 1659507542621651001n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('retrieves trace entry from real timestamp', () => {
+ const entry = parser.getEntry(1, TimestampType.REAL);
+ expect(BigInt((entry as any).elapsedRealtimeNanos)).toEqual(2517952515n);
+ });
+
+ it("decodes 'what' field in proto", () => {
+ {
+ const entry = parser.getEntry(0, TimestampType.REAL) as any;
+ expect(entry.transactions[0].layerChanges[0].what).toEqual('eLayerChanged');
+ expect(entry.transactions[1].layerChanges[0].what).toEqual(
+ 'eFlagsChanged | eDestinationFrameChanged'
+ );
+ }
+ {
+ const entry = parser.getEntry(222, TimestampType.REAL) as any;
+ expect(entry.transactions[1].displayChanges[0].what).toEqual(
+ 'eLayerStackChanged | eDisplayProjectionChanged | eFlagsChanged'
+ );
+ }
+ });
+ });
+
+ describe('trace with elapsed (only) timestamp', () => {
+ let parser: Parser<object>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/Transactions.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(4997);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 14862317023n),
+ new Timestamp(TimestampType.ELAPSED, 14873423549n),
+ new Timestamp(TimestampType.ELAPSED, 14884850511n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it("doesn't provide real timestamps", () => {
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_transitions_shell.ts b/tools/winscope/src/parsers/parser_transitions_shell.ts
new file mode 100644
index 0000000..db7941b
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_transitions_shell.ts
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 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 {
+ CrossPlatform,
+ ShellTransitionData,
+ Transition,
+ WmTransitionData,
+} from 'trace/flickerlib/common';
+import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {ShellTransitionsTraceFileProto} from './proto_types';
+
+export class ParserTransitionsShell extends AbstractParser {
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private handlerMapping: undefined | Map<number, string>;
+
+ override getTraceType(): TraceType {
+ return TraceType.SHELL_TRANSITION;
+ }
+
+ override decodeTrace(traceBuffer: Uint8Array): Transition[] {
+ const decodedProto = ShellTransitionsTraceFileProto.decode(traceBuffer) as any;
+
+ if (Object.prototype.hasOwnProperty.call(decodedProto, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos);
+ } else {
+ console.warn('Missing realToElapsedTimeOffsetNanos property on SF trace proto');
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ this.handlerMapping = new Map<number, string>();
+ for (const mapping of decodedProto.handlerMappings) {
+ this.handlerMapping.set(mapping.id, mapping.name);
+ }
+
+ return decodedProto.transitions;
+ }
+
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any {
+ return this.parseShellTransitionEntry(entryProto);
+ }
+
+ private parseShellTransitionEntry(entry: any): Transition {
+ this.validateShellTransitionEntry(entry);
+
+ if (this.realToElapsedTimeOffsetNs === undefined) {
+ throw new Error('missing realToElapsedTimeOffsetNs');
+ }
+
+ let dispatchTime = null;
+ if (entry.dispatchTimeNs && BigInt(entry.dispatchTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.dispatchTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ dispatchTime = CrossPlatform.timestamp.fromString(
+ entry.dispatchTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let mergeRequestTime = null;
+ if (entry.mergeRequestTimeNs && BigInt(entry.mergeRequestTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.mergeRequestTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ mergeRequestTime = CrossPlatform.timestamp.fromString(
+ entry.mergeRequestTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let mergeTime = null;
+ if (entry.mergeTimeNs && BigInt(entry.mergeTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.mergeTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ mergeTime = CrossPlatform.timestamp.fromString(
+ entry.mergeTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let abortTime = null;
+ if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ abortTime = CrossPlatform.timestamp.fromString(
+ entry.abortTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let mergedInto = null;
+ if (entry.mergedInto !== 0) {
+ mergedInto = entry.mergedInto;
+ }
+
+ if (this.handlerMapping === undefined) {
+ throw new Error('Missing handler mapping!');
+ }
+
+ return new Transition(
+ entry.id,
+ new WmTransitionData(),
+ new ShellTransitionData(
+ dispatchTime,
+ mergeRequestTime,
+ mergeTime,
+ abortTime,
+ this.handlerMapping.get(entry.handler),
+ mergedInto
+ )
+ );
+ }
+
+ private validateShellTransitionEntry(entry: any) {
+ if (entry.id === 0) {
+ throw new Error('Entry need a non null id');
+ }
+ if (
+ !entry.dispatchTimeNs &&
+ !entry.mergeRequestTimeNs &&
+ !entry.mergeTimeNs &&
+ !entry.abortTimeNs
+ ) {
+ throw new Error('Requires at least one non-null timestamp');
+ }
+ }
+
+ protected getMagicNumber(): number[] | undefined {
+ return [0x09, 0x57, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WMSTRACE
+ }
+
+ override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp {
+ decodedEntry = this.parseShellTransitionEntry(decodedEntry);
+
+ if (type === TimestampType.ELAPSED) {
+ return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString()));
+ }
+
+ if (type === TimestampType.REAL) {
+ return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString()));
+ }
+
+ throw new Error('Timestamp type unsupported');
+ }
+}
diff --git a/tools/winscope/src/parsers/parser_transitions_shell_test.ts b/tools/winscope/src/parsers/parser_transitions_shell_test.ts
new file mode 100644
index 0000000..f986db7
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_transitions_shell_test.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ShellFileParserTransitions', () => {
+ let parser: Parser<object>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.SHELL_TRANSITION);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(6);
+
+ const expected = [
+ new ElapsedTimestamp(57649649922341n),
+ new ElapsedTimestamp(57649829445249n),
+ new ElapsedTimestamp(57649829526223n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new RealTimestamp(1683188477607285317n),
+ new RealTimestamp(1683188477786808225n),
+ new RealTimestamp(1683188477786889199n),
+ ];
+
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+
+ expect(timestamps.length).toEqual(6);
+
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_transitions_wm.ts b/tools/winscope/src/parsers/parser_transitions_wm.ts
new file mode 100644
index 0000000..0fce3f6
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_transitions_wm.ts
@@ -0,0 +1,149 @@
+import {
+ CrossPlatform,
+ ShellTransitionData,
+ Transition,
+ TransitionChange,
+ TransitionType,
+ WmTransitionData,
+} from 'trace/flickerlib/common';
+import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {WmTransitionsTraceFileProto} from './proto_types';
+
+export class ParserTransitionsWm extends AbstractParser {
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+
+ override getTraceType(): TraceType {
+ return TraceType.WM_TRANSITION;
+ }
+
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any {
+ return this.parseWmTransitionEntry(entryProto);
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decodedProto = WmTransitionsTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decodedProto, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos);
+ } else {
+ console.warn('Missing realToElapsedTimeOffsetNanos property on SF trace proto');
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decodedProto.transitions;
+ }
+
+ override getMagicNumber(): number[] | undefined {
+ return [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE
+ }
+
+ override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp {
+ decodedEntry = this.parseWmTransitionEntry(decodedEntry);
+
+ if (type === TimestampType.ELAPSED) {
+ return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString()));
+ }
+
+ if (type === TimestampType.REAL) {
+ return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString()));
+ }
+
+ throw new Error('Timestamp type unsupported');
+ }
+
+ private parseWmTransitionEntry(entry: any): Transition {
+ this.validateWmTransitionEntry(entry);
+ let changes: TransitionChange[] | null;
+ if (entry.targets.length === 0) {
+ changes = null;
+ } else {
+ changes = entry.targets.map(
+ (it: any) =>
+ new TransitionChange(TransitionType.Companion.fromInt(it.mode), it.layerId, it.windowId)
+ );
+ }
+
+ if (this.realToElapsedTimeOffsetNs === undefined) {
+ throw new Error('missing realToElapsedTimeOffsetNs');
+ }
+
+ let createTime = null;
+ if (entry.createTimeNs && BigInt(entry.createTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.createTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ createTime = CrossPlatform.timestamp.fromString(
+ entry.createTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let sendTime = null;
+ if (entry.sendTimeNs && BigInt(entry.sendTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.sendTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ sendTime = CrossPlatform.timestamp.fromString(
+ entry.sendTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let abortTime = null;
+ if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ abortTime = CrossPlatform.timestamp.fromString(
+ entry.abortTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let finishTime = null;
+ if (entry.finishTimeNs && BigInt(entry.finishTimeNs.toString()) !== 0n) {
+ const unixNs = BigInt(entry.finishTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
+ finishTime = CrossPlatform.timestamp.fromString(
+ entry.finishTimeNs.toString(),
+ null,
+ unixNs.toString()
+ );
+ }
+
+ let startTransactionId = null;
+ if (entry.startTransactionId && BigInt(entry.startTransactionId.toString()) !== 0n) {
+ startTransactionId = BigInt(entry.startTransactionId.toString());
+ }
+
+ let finishTransactionId = null;
+ if (entry.finishTransactionId && BigInt(entry.finishTransactionId.toString()) !== 0n) {
+ finishTransactionId = BigInt(entry.finishTransactionId.toString());
+ }
+
+ let type = null;
+ if (entry.type !== 0) {
+ type = TransitionType.Companion.fromInt(entry.type);
+ }
+
+ return new Transition(
+ entry.id,
+ new WmTransitionData(
+ createTime,
+ sendTime,
+ abortTime,
+ finishTime,
+ startTransactionId?.toString(),
+ finishTransactionId?.toString(),
+ type,
+ changes
+ ),
+ new ShellTransitionData()
+ );
+ }
+
+ private validateWmTransitionEntry(entry: any) {
+ if (entry.id === 0) {
+ throw new Error('Entry need a non null id');
+ }
+ if (!entry.createTimeNs && !entry.sendTimeNs && !entry.abortTimeNs && !entry.finishTimeNs) {
+ throw new Error('Requires at least one non-null timestamp');
+ }
+ }
+}
diff --git a/tools/winscope/src/parsers/parser_transitions_wm_test.ts b/tools/winscope/src/parsers/parser_transitions_wm_test.ts
new file mode 100644
index 0000000..0f0cdd1
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_transitions_wm_test.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('WmFileParserTransitions', () => {
+ let parser: Parser<object>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/wm_transition_trace.pb'
+ );
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.WM_TRANSITION);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(8);
+
+ const expected = [
+ new ElapsedTimestamp(57649586217344n),
+ new ElapsedTimestamp(57649691956439n),
+ new ElapsedTimestamp(57650183020323n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new RealTimestamp(1683188477542869667n),
+ new RealTimestamp(1683188477648608762n),
+ new RealTimestamp(1683188478139672646n),
+ ];
+
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+
+ expect(timestamps.length).toEqual(8);
+
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_window_manager.ts b/tools/winscope/src/parsers/parser_window_manager.ts
new file mode 100644
index 0000000..8e64f88
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_window_manager.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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 {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {WindowManagerTraceFileProto} from './proto_types';
+
+class ParserWindowManager extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.WINDOW_MANAGER;
+ }
+
+ override getMagicNumber(): number[] {
+ return ParserWindowManager.MAGIC_NUMBER;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const decoded = WindowManagerTraceFileProto.decode(buffer) as any;
+ if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) {
+ this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
+ } else {
+ this.realToElapsedTimeOffsetNs = undefined;
+ }
+ return decoded.entry;
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
+ } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(
+ type,
+ this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos)
+ );
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: any
+ ): WindowManagerState {
+ return WindowManagerState.fromProto(
+ entryProto.windowManagerService,
+ BigInt(entryProto.elapsedRealtimeNanos.toString()),
+ entryProto.where,
+ this.realToElapsedTimeOffsetNs,
+ timestampType === TimestampType.ELAPSED /*useElapsedTime*/
+ );
+ }
+
+ private realToElapsedTimeOffsetNs: undefined | bigint;
+ private static readonly MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
+}
+
+export {ParserWindowManager};
diff --git a/tools/winscope/src/parsers/parser_window_manager_dump.ts b/tools/winscope/src/parsers/parser_window_manager_dump.ts
new file mode 100644
index 0000000..29b0c18
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_window_manager_dump.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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 {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+import {AbstractParser} from './abstract_parser';
+import {WindowManagerServiceDumpProto} from './proto_types';
+
+class ParserWindowManagerDump extends AbstractParser {
+ constructor(trace: TraceFile) {
+ super(trace);
+ }
+
+ override getTraceType(): TraceType {
+ return TraceType.WINDOW_MANAGER;
+ }
+
+ override getMagicNumber(): undefined {
+ return undefined;
+ }
+
+ override decodeTrace(buffer: Uint8Array): any[] {
+ const entryProto = WindowManagerServiceDumpProto.decode(buffer);
+
+ // This parser is prone to accepting invalid inputs because it lacks a magic
+ // number. Let's reduce the chances of accepting invalid inputs by making
+ // sure that a trace entry can actually be created from the decoded proto.
+ // If the trace entry creation fails, an exception is thrown and the parser
+ // will be considered unsuited for this input data.
+ this.processDecodedEntry(0, TimestampType.ELAPSED /*irrelevant for dump*/, entryProto);
+
+ return [entryProto];
+ }
+
+ override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(TimestampType.ELAPSED, 0n);
+ } else if (type === TimestampType.REAL) {
+ return new Timestamp(TimestampType.REAL, 0n);
+ }
+ return undefined;
+ }
+
+ override processDecodedEntry(
+ index: number,
+ timestampType: TimestampType,
+ entryProto: any
+ ): WindowManagerState {
+ return WindowManagerState.fromProto(entryProto);
+ }
+}
+
+export {ParserWindowManagerDump};
diff --git a/tools/winscope/src/parsers/parser_window_manager_dump_test.ts b/tools/winscope/src/parsers/parser_window_manager_dump_test.ts
new file mode 100644
index 0000000..529a50a
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_window_manager_dump_test.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserWindowManagerDump', () => {
+ let parser: Parser<WindowManagerState>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/dump_WindowManager.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
+ });
+
+ it('provides elapsed timestamp (always zero)', () => {
+ const expected = [new Timestamp(TimestampType.ELAPSED, 0n)];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ });
+
+ it('provides real timestamp (always zero)', () => {
+ const expected = [new Timestamp(TimestampType.REAL, 0n)];
+ expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ const entry = parser.getEntry(0, TimestampType.ELAPSED);
+ expect(entry).toBeInstanceOf(WindowManagerState);
+ expect(BigInt(entry.timestamp.elapsedNanos.toString())).toEqual(0n);
+ });
+});
diff --git a/tools/winscope/src/parsers/parser_window_manager_test.ts b/tools/winscope/src/parsers/parser_window_manager_test.ts
new file mode 100644
index 0000000..10c5787
--- /dev/null
+++ b/tools/winscope/src/parsers/parser_window_manager_test.ts
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+
+describe('ParserWindowManager', () => {
+ describe('trace with elapsed + real timestamp', () => {
+ let parser: Parser<WindowManagerState>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/WindowManager.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 14474594000n),
+ new Timestamp(TimestampType.ELAPSED, 15398076788n),
+ new Timestamp(TimestampType.ELAPSED, 15409222011n),
+ ];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1659107089075566202n),
+ new Timestamp(TimestampType.REAL, 1659107089999048990n),
+ new Timestamp(TimestampType.REAL, 1659107090010194213n),
+ ];
+ expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ const entry = parser.getEntry(1, TimestampType.REAL);
+ expect(entry).toBeInstanceOf(WindowManagerState);
+ expect(BigInt(entry.timestamp.elapsedNanos.toString())).toEqual(15398076788n);
+ expect(BigInt(entry.timestamp.unixNanos.toString())).toEqual(1659107089999048990n);
+ });
+
+ it('formats entry timestamps', () => {
+ const entry = parser.getEntry(1, TimestampType.REAL);
+ expect(entry.name).toEqual('2022-07-29T15:04:49.999048960');
+ });
+ });
+
+ describe('trace elapsed (only) timestamp', () => {
+ let parser: Parser<WindowManagerState>;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/WindowManager.pb');
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
+ });
+
+ it('provides timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 850254319343n),
+ new Timestamp(TimestampType.ELAPSED, 850763506110n),
+ new Timestamp(TimestampType.ELAPSED, 850782750048n),
+ ];
+ expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ });
+
+ it('retrieves trace entry', () => {
+ const entry = parser.getEntry(0, TimestampType.ELAPSED);
+ expect(entry).toBeInstanceOf(WindowManagerState);
+ expect(BigInt(entry.timestamp.elapsedNanos.toString())).toEqual(850254319343n);
+ });
+
+ it('formats entry timestamps', () => {
+ const entry = parser.getEntry(0, TimestampType.ELAPSED);
+ expect(entry.name).toEqual('14m10s254ms319343ns');
+ });
+ });
+});
diff --git a/tools/winscope/src/parsers/proto_types.js b/tools/winscope/src/parsers/proto_types.js
new file mode 100644
index 0000000..3152bd8
--- /dev/null
+++ b/tools/winscope/src/parsers/proto_types.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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 Long from 'long';
+import * as protobuf from 'protobufjs';
+
+protobuf.util.Long = Long; // otherwise 64-bit types would be decoded as javascript number (only 53-bits precision)
+protobuf.configure();
+
+import protoLogJson from 'frameworks/base/core/proto/android/internal/protolog.proto';
+import accessibilityJson from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto';
+import windowManagerJson from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
+import wmTransitionsJson from 'frameworks/base/core/proto/android/server/windowmanagertransitiontrace.proto';
+import inputMethodClientsJson from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
+import shellTransitionsJson from 'frameworks/base/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto';
+import layersJson from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
+import transactionsJson from 'frameworks/native/services/surfaceflinger/layerproto/transactions.proto';
+
+const AccessibilityTraceFileProto = protobuf.Root.fromJSON(accessibilityJson).lookupType(
+ 'com.android.server.accessibility.AccessibilityTraceFileProto'
+);
+const InputMethodClientsTraceFileProto = protobuf.Root.fromJSON(inputMethodClientsJson).lookupType(
+ 'android.view.inputmethod.InputMethodClientsTraceFileProto'
+);
+const InputMethodManagerServiceTraceFileProto = protobuf.Root.fromJSON(
+ inputMethodClientsJson
+).lookupType('android.view.inputmethod.InputMethodManagerServiceTraceFileProto');
+const InputMethodServiceTraceFileProto = protobuf.Root.fromJSON(inputMethodClientsJson).lookupType(
+ 'android.view.inputmethod.InputMethodServiceTraceFileProto'
+);
+const LayersTraceFileProto = protobuf.Root.fromJSON(layersJson).lookupType(
+ 'android.surfaceflinger.LayersTraceFileProto'
+);
+const ProtoLogFileProto = protobuf.Root.fromJSON(protoLogJson).lookupType(
+ 'com.android.internal.protolog.ProtoLogFileProto'
+);
+const TransactionsTraceFileProto = protobuf.Root.fromJSON(transactionsJson).lookupType(
+ 'android.surfaceflinger.proto.TransactionTraceFile'
+);
+const WindowManagerServiceDumpProto = protobuf.Root.fromJSON(windowManagerJson).lookupType(
+ 'com.android.server.wm.WindowManagerServiceDumpProto'
+);
+const WindowManagerTraceFileProto = protobuf.Root.fromJSON(windowManagerJson).lookupType(
+ 'com.android.server.wm.WindowManagerTraceFileProto'
+);
+const WmTransitionsTraceFileProto = protobuf.Root.fromJSON(wmTransitionsJson).lookupType(
+ 'com.android.server.wm.shell.TransitionTraceProto'
+);
+const ShellTransitionsTraceFileProto = protobuf.Root.fromJSON(shellTransitionsJson).lookupType(
+ 'com.android.wm.shell.WmShellTransitionTraceProto'
+);
+
+export {
+ AccessibilityTraceFileProto,
+ InputMethodClientsTraceFileProto,
+ InputMethodManagerServiceTraceFileProto,
+ InputMethodServiceTraceFileProto,
+ LayersTraceFileProto,
+ ProtoLogFileProto,
+ TransactionsTraceFileProto,
+ WindowManagerServiceDumpProto,
+ WindowManagerTraceFileProto,
+ WmTransitionsTraceFileProto,
+ ShellTransitionsTraceFileProto,
+};
diff --git a/tools/winscope/src/parsers/traces_parser_cujs.ts b/tools/winscope/src/parsers/traces_parser_cujs.ts
new file mode 100644
index 0000000..cd82eed
--- /dev/null
+++ b/tools/winscope/src/parsers/traces_parser_cujs.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023, 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 {Cuj, CujTrace, EventLog, Transition} from 'trace/flickerlib/common';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {AbstractTracesParser} from './abstract_traces_parser';
+import {ParserEventLog} from './parser_eventlog';
+
+export class TracesParserCujs extends AbstractTracesParser<Transition> {
+ private readonly eventLogTrace: ParserEventLog | undefined;
+ private readonly descriptors: string[];
+
+ constructor(parsers: Array<Parser<object>>) {
+ super(parsers);
+
+ const eventlogTraces = this.parsers.filter((it) => it.getTraceType() === TraceType.EVENT_LOG);
+ if (eventlogTraces.length > 0) {
+ this.eventLogTrace = eventlogTraces[0] as ParserEventLog;
+ }
+
+ if (this.eventLogTrace !== undefined) {
+ this.descriptors = this.eventLogTrace.getDescriptors();
+ } else {
+ this.descriptors = [];
+ }
+ }
+
+ override canProvideEntries(): boolean {
+ return this.eventLogTrace !== undefined;
+ }
+
+ getLengthEntries(): number {
+ return this.getDecodedEntries().length;
+ }
+
+ getEntry(index: number, timestampType: TimestampType): Transition {
+ return this.getDecodedEntries()[index];
+ }
+
+ private cujTrace: CujTrace | undefined;
+ getDecodedEntries(): Cuj[] {
+ if (this.eventLogTrace === undefined) {
+ throw new Error('eventLogTrace not defined');
+ }
+
+ if (this.cujTrace === undefined) {
+ const events: Event[] = [];
+
+ for (let i = 0; i < this.eventLogTrace.getLengthEntries(); i++) {
+ events.push(this.eventLogTrace.getEntry(i, TimestampType.REAL));
+ }
+
+ this.cujTrace = new EventLog(events).cujTrace;
+ }
+
+ return this.cujTrace.entries;
+ }
+
+ override getDescriptors(): string[] {
+ return this.descriptors;
+ }
+
+ getTraceType(): TraceType {
+ return TraceType.CUJS;
+ }
+
+ override getTimestamp(type: TimestampType, transition: Transition): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(transition.timestamp.elapsedNanos.toString()));
+ } else if (type === TimestampType.REAL) {
+ return new Timestamp(type, BigInt(transition.timestamp.unixNanos.toString()));
+ }
+ return undefined;
+ }
+}
diff --git a/tools/winscope/src/parsers/traces_parser_cujs_test.ts b/tools/winscope/src/parsers/traces_parser_cujs_test.ts
new file mode 100644
index 0000000..41b0997
--- /dev/null
+++ b/tools/winscope/src/parsers/traces_parser_cujs_test.ts
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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 {assertDefined} from 'common/assert_utils';
+import {UnitTestUtils} from 'test/unit/utils';
+import {Cuj} from 'trace/flickerlib/common';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {TracesParserCujs} from './traces_parser_cujs';
+
+describe('ParserCujs', () => {
+ let parser: Parser<Cuj>;
+
+ beforeAll(async () => {
+ const eventLogParser = assertDefined(
+ await UnitTestUtils.getParser('traces/eventlog.winscope')
+ ) as Parser<Event>;
+
+ parser = new TracesParserCujs([eventLogParser]);
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.CUJS);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(16);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 2661012770462n),
+ new Timestamp(TimestampType.ELAPSED, 2661012874914n),
+ new Timestamp(TimestampType.ELAPSED, 2661012903966n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1681207048025446000n),
+ new Timestamp(TimestampType.REAL, 1681207048025551000n),
+ new Timestamp(TimestampType.REAL, 1681207048025580000n),
+ ];
+
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+
+ expect(timestamps.length).toEqual(16);
+
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/parsers/traces_parser_transitions.ts b/tools/winscope/src/parsers/traces_parser_transitions.ts
new file mode 100644
index 0000000..b751f4e
--- /dev/null
+++ b/tools/winscope/src/parsers/traces_parser_transitions.ts
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023, 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 {Transition, TransitionsTrace} from 'trace/flickerlib/common';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {AbstractTracesParser} from './abstract_traces_parser';
+
+export class TracesParserTransitions extends AbstractTracesParser<Transition> {
+ private readonly wmTransitionTrace: Parser<object> | undefined;
+ private readonly shellTransitionTrace: Parser<object> | undefined;
+ private readonly descriptors: string[];
+
+ constructor(parsers: Array<Parser<object>>) {
+ super(parsers);
+
+ const wmTransitionTraces = this.parsers.filter(
+ (it) => it.getTraceType() === TraceType.WM_TRANSITION
+ );
+ if (wmTransitionTraces.length > 0) {
+ this.wmTransitionTrace = wmTransitionTraces[0];
+ }
+ const shellTransitionTraces = this.parsers.filter(
+ (it) => it.getTraceType() === TraceType.SHELL_TRANSITION
+ );
+ if (shellTransitionTraces.length > 0) {
+ this.shellTransitionTrace = shellTransitionTraces[0];
+ }
+ if (this.wmTransitionTrace !== undefined && this.shellTransitionTrace !== undefined) {
+ this.descriptors = this.wmTransitionTrace
+ .getDescriptors()
+ .concat(this.shellTransitionTrace.getDescriptors());
+ } else {
+ this.descriptors = [];
+ }
+ }
+
+ override canProvideEntries(): boolean {
+ return this.wmTransitionTrace !== undefined && this.shellTransitionTrace !== undefined;
+ }
+
+ getLengthEntries(): number {
+ return this.getDecodedEntries().length;
+ }
+
+ getEntry(index: number, timestampType: TimestampType): Transition {
+ return this.getDecodedEntries()[index];
+ }
+
+ private decodedEntries: Transition[] | undefined;
+ getDecodedEntries(): Transition[] {
+ if (this.decodedEntries === undefined) {
+ if (this.wmTransitionTrace === undefined) {
+ throw new Error('Missing WM Transition trace');
+ }
+
+ if (this.shellTransitionTrace === undefined) {
+ throw new Error('Missing Shell Transition trace');
+ }
+
+ const wmTransitionEntries: Transition[] = [];
+ for (let index = 0; index < this.wmTransitionTrace.getLengthEntries(); index++) {
+ wmTransitionEntries.push(this.wmTransitionTrace.getEntry(index, TimestampType.REAL));
+ }
+
+ const shellTransitionEntries: Transition[] = [];
+ for (let index = 0; index < this.shellTransitionTrace.getLengthEntries(); index++) {
+ shellTransitionEntries.push(this.shellTransitionTrace.getEntry(index, TimestampType.REAL));
+ }
+
+ const transitionsTrace = new TransitionsTrace(
+ wmTransitionEntries.concat(shellTransitionEntries)
+ );
+
+ this.decodedEntries = transitionsTrace.asCompressed().entries as Transition[];
+ }
+
+ return this.decodedEntries;
+ }
+
+ override getDescriptors(): string[] {
+ return this.descriptors;
+ }
+
+ getTraceType(): TraceType {
+ return TraceType.TRANSITION;
+ }
+
+ override getTimestamp(type: TimestampType, transition: Transition): undefined | Timestamp {
+ if (type === TimestampType.ELAPSED) {
+ return new Timestamp(type, BigInt(transition.timestamp.elapsedNanos.toString()));
+ } else if (type === TimestampType.REAL) {
+ return new Timestamp(type, BigInt(transition.timestamp.unixNanos.toString()));
+ }
+ return undefined;
+ }
+}
diff --git a/tools/winscope/src/parsers/traces_parser_transitions_test.ts b/tools/winscope/src/parsers/traces_parser_transitions_test.ts
new file mode 100644
index 0000000..4e811c5
--- /dev/null
+++ b/tools/winscope/src/parsers/traces_parser_transitions_test.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 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 {UnitTestUtils} from 'test/unit/utils';
+import {Transition} from 'trace/flickerlib/common';
+import {Parser} from 'trace/parser';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {TraceType} from 'trace/trace_type';
+import {TracesParserTransitions} from './traces_parser_transitions';
+
+describe('ParserTransitions', () => {
+ let parser: Parser<Transition>;
+
+ beforeAll(async () => {
+ const wmSideParser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/wm_transition_trace.pb'
+ );
+ const shellSideParser = await UnitTestUtils.getParser(
+ 'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
+ );
+
+ parser = new TracesParserTransitions([wmSideParser, shellSideParser]);
+ });
+
+ it('has expected trace type', () => {
+ expect(parser.getTraceType()).toEqual(TraceType.TRANSITION);
+ });
+
+ it('provides elapsed timestamps', () => {
+ const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
+
+ expect(timestamps.length).toEqual(4);
+
+ const expected = [
+ new Timestamp(TimestampType.ELAPSED, 57649586217344n),
+ new Timestamp(TimestampType.ELAPSED, 57649691956439n),
+ new Timestamp(TimestampType.ELAPSED, 57651263812071n),
+ ];
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+
+ it('provides real timestamps', () => {
+ const expected = [
+ new Timestamp(TimestampType.REAL, 1683188477542869667n),
+ new Timestamp(TimestampType.REAL, 1683188477648608762n),
+ new Timestamp(TimestampType.REAL, 1683188479220464394n),
+ ];
+
+ const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+
+ expect(timestamps.length).toEqual(4);
+
+ expect(timestamps.slice(0, 3)).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/polyfills.ts b/tools/winscope/src/polyfills.ts
new file mode 100644
index 0000000..e4555ed
--- /dev/null
+++ b/tools/winscope/src/polyfills.ts
@@ -0,0 +1,52 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes recent versions of Safari, Chrome (including
+ * Opera), Edge on the desktop, and iOS and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/tools/winscope/src/proxyclient/ProxyClient.ts b/tools/winscope/src/proxyclient/ProxyClient.ts
deleted file mode 100644
index 95f32a1..0000000
--- a/tools/winscope/src/proxyclient/ProxyClient.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2022, 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 LocalStore from '../localstore.js';
-import {FILE_DECODERS, FILE_TYPES} from '../decode.js';
-
-export enum ProxyState {
- ERROR = 0,
- CONNECTING = 1,
- NO_PROXY = 2,
- INVALID_VERSION = 3,
- UNAUTH = 4,
- DEVICES = 5,
- START_TRACE = 6,
- END_TRACE = 7,
- LOAD_DATA = 8,
-};
-
-export enum ProxyEndpoint {
- DEVICES = '/devices/',
- START_TRACE = '/start/',
- END_TRACE = '/end/',
- CONFIG_TRACE = '/configtrace/',
- SELECTED_WM_CONFIG_TRACE = '/selectedwmconfigtrace/',
- SELECTED_SF_CONFIG_TRACE = '/selectedsfconfigtrace/',
- DUMP = '/dump/',
- FETCH = '/fetch/',
- STATUS = '/status/',
- CHECK_WAYLAND = '/checkwayland/',
-};
-
-const proxyFileTypeAdapter = {
- 'window_trace': FILE_TYPES.WINDOW_MANAGER_TRACE,
- 'accessibility_trace': FILE_TYPES.ACCESSIBILITY_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,
- 'transactions_legacy': FILE_TYPES.TRANSACTIONS_TRACE_LEGACY,
- '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,
-};
-
-class ProxyClient {
- readonly WINSCOPE_PROXY_URL = 'http://localhost:5544';
- readonly VERSION = '0.8';
-
- store:LocalStore = LocalStore('adb', {
- proxyKey: '',
- lastDevice: ''});
-
- state:ProxyState = ProxyState.CONNECTING;
- stateChangeListeners:{(param:ProxyState, errorText:String): void;}[] = [];
- refresh_worker: NodeJS.Timer = null;
- devices = {};
- selectedDevice = ""
- errorText:String = ""
-
- call(method, path, view, onSuccess, type = null, jsonRequest = null) {
- const request = new XMLHttpRequest();
- let client = this;
- request.onreadystatechange = function() {
- if (this.readyState !== 4) {
- return;
- }
- if (this.status === 0) {
- client.setState(ProxyState.NO_PROXY);
- } else if (this.status === 200) {
- if (this.getResponseHeader('Winscope-Proxy-Version') !== client.VERSION) {
- client.setState(ProxyState.INVALID_VERSION);
- } else if (onSuccess) {
- onSuccess(this, view);
- }
- } else if (this.status === 403) {
- client.setState(ProxyState.UNAUTH);
- } else {
- if (this.responseType === 'text' || !this.responseType) {
- client.errorText = this.responseText;
- } else if (this.responseType === 'arraybuffer') {
- client.errorText = String.fromCharCode.apply(null, new Uint8Array(this.response));
- }
- client.setState(ProxyState.ERROR);
- }
- };
- request.responseType = type || "";
- request.open(method, client.WINSCOPE_PROXY_URL + path);
- request.setRequestHeader('Winscope-Token', client.store.proxyKey);
- if (jsonRequest) {
- const json = JSON.stringify(jsonRequest);
- request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
- request.send(json);
- } else {
- request.send();
- }
- }
-
- setState(state:ProxyState, errorText:String = "") {
- this.state = state;
- this.errorText = errorText;
- for (let listener of this.stateChangeListeners) {
- listener(state, errorText);
- }
- }
-
- onStateChange(fn: (state:ProxyState, errorText:String) => void) {
- this.removeOnStateChange(fn);
- this.stateChangeListeners.push(fn);
- }
-
- removeOnStateChange(removeFn: (state:ProxyState, errorText:String) => void) {
- this.stateChangeListeners = this.stateChangeListeners.filter(fn => fn !== removeFn);
- }
-
- getDevices() {
- if (this.state !== ProxyState.DEVICES && this.state !== ProxyState.CONNECTING) {
- clearInterval(this.refresh_worker);
- this.refresh_worker = null;
- return;
- }
- let client = this;
- this.call('GET', ProxyEndpoint.DEVICES, this, function(request, view) {
- try {
- client.devices = JSON.parse(request.responseText);
- if (client.store.lastDevice && client.devices[client.store.lastDevice] &&
- client.devices[client.store.lastDevice].authorised) {
- client.selectDevice(client.store.lastDevice);
- } else {
- if (client.refresh_worker === null) {
- client.refresh_worker = setInterval(client.getDevices, 1000);
- }
- client.setState(ProxyState.DEVICES);
- }
- } catch (err) {
- console.error(err);
- client.errorText = request.responseText;
- client.setState(ProxyState.ERROR);
- }
- });
- }
-
- selectDevice(device_id) {
- this.selectedDevice = device_id;
- this.store.lastDevice = device_id;
- this.setState(ProxyState.START_TRACE);
- }
-
- deviceId() {
- return this.selectedDevice;
- }
-
- resetLastDevice() {
- this.store.lastDevice = '';
- }
-
- loadFile(files, idx, traceType, view) {
- let client = this;
- this.call('GET', `${ProxyEndpoint.FETCH}${proxyClient.deviceId()}/${files[idx]}/`, view,
- (request, view) => {
- try {
- 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) {
- client.loadFile(files, idx + 1, traceType, view);
- } else {
- const currentDate = new Date().toISOString();
- view.$emit('dataReady',
- `winscope-${traceType}-${currentDate}`,
- view.dataFiles);
- }
- } catch (err) {
- console.error(err);
- client.setState(ProxyState.ERROR, err);
- }
- }, 'arraybuffer');
- }
-
-}
-
-export const proxyClient = new ProxyClient();
diff --git a/tools/winscope/src/styles.css b/tools/winscope/src/styles.css
new file mode 100644
index 0000000..fadb052
--- /dev/null
+++ b/tools/winscope/src/styles.css
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+html, body, app-root {
+ height: 100%;
+ width: 100%;
+}
+
+body {
+ margin: 0;
+}
+
+app-root {
+ display: flex;
+ flex-direction: column;
+}
+
+.mat-headline, .mat-title, .mat-subheading-2, .mat-body-1, .mat-body-2, .mat-small {
+ margin: 0;
+}
+
+.card-grid {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ overflow: auto;
+}
diff --git a/tools/winscope/src/test/common/file_impl.ts b/tools/winscope/src/test/common/file_impl.ts
new file mode 100644
index 0000000..315bbe1
--- /dev/null
+++ b/tools/winscope/src/test/common/file_impl.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// This class is needed for unit tests because Node.js doesn't provide
+// an implementation of the Web API's File type
+class FileImpl {
+ readonly size: number;
+ readonly type: string;
+ readonly name: string;
+ readonly lastModified: number = 0;
+ readonly webkitRelativePath: string = '';
+ private readonly buffer: ArrayBuffer;
+
+ constructor(buffer: ArrayBuffer, fileName: string) {
+ this.buffer = buffer;
+ this.size = this.buffer.byteLength;
+ this.type = 'application/octet-stream';
+ this.name = fileName;
+ }
+
+ arrayBuffer(): Promise<ArrayBuffer> {
+ return new Promise<ArrayBuffer>((resolve) => {
+ resolve(this.buffer);
+ });
+ }
+
+ slice(start?: number, end?: number, contentType?: string): Blob {
+ throw new Error('Not implemented!');
+ }
+
+ stream(): any {
+ throw new Error('Not implemented!');
+ }
+
+ text(): Promise<string> {
+ const utf8Decoder = new TextDecoder();
+ const text = utf8Decoder.decode(this.buffer);
+ return new Promise<string>((resolve) => {
+ resolve(text);
+ });
+ }
+}
+
+export {FileImpl};
diff --git a/tools/winscope/src/test/common/utils.ts b/tools/winscope/src/test/common/utils.ts
new file mode 100644
index 0000000..8c81a63
--- /dev/null
+++ b/tools/winscope/src/test/common/utils.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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 * as fs from 'fs';
+import * as path from 'path';
+import {FileImpl} from './file_impl';
+
+class CommonTestUtils {
+ static async getFixtureFile(
+ srcFilename: string,
+ dstFilename: string = srcFilename
+ ): Promise<File> {
+ const buffer = CommonTestUtils.loadFixture(srcFilename);
+ return new FileImpl(buffer, dstFilename) as unknown as File;
+ }
+
+ static loadFixture(filename: string): ArrayBuffer {
+ return fs.readFileSync(CommonTestUtils.getFixturePath(filename));
+ }
+
+ static getFixturePath(filename: string): string {
+ if (path.isAbsolute(filename)) {
+ return filename;
+ }
+ return path.join(CommonTestUtils.getProjectRootPath(), 'src/test/fixtures', filename);
+ }
+
+ static getProjectRootPath(): string {
+ let root = __dirname;
+ while (path.basename(root) !== 'winscope') {
+ root = path.dirname(root);
+ }
+ return root;
+ }
+}
+
+export {CommonTestUtils};
diff --git a/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts b/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts
new file mode 100644
index 0000000..86ea574
--- /dev/null
+++ b/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element, ElementFinder} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Cross-Tool Protocol', () => {
+ const WINSCOPE_URL = 'http://localhost:8080';
+ const REMOTE_TOOL_MOCK_URL = 'http://localhost:8081';
+
+ const TIMESTAMP_IN_BUGREPORT_MESSAGE = '1670509911000000000';
+ const TIMESTAMP_FROM_REMOTE_TOOL_TO_WINSCOPE = '1670509912000000000';
+ const TIMESTAMP_FROM_WINSCOPE_TO_REMOTE_TOOL = '1670509913000000000';
+
+ beforeAll(async () => {
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
+ await browser.manage().timeouts().implicitlyWait(15000);
+ await checkServerIsUp('Remote tool mock', REMOTE_TOOL_MOCK_URL);
+ await checkServerIsUp('Winscope', WINSCOPE_URL);
+ });
+
+ beforeEach(async () => {
+ await browser.get(REMOTE_TOOL_MOCK_URL);
+ });
+
+ it('allows communication between remote tool and Winscope', async () => {
+ await openWinscopeTabFromRemoteTool();
+ await waitWinscopeTabIsOpen();
+
+ await sendBugreportToWinscope();
+ await checkWinscopeRendersUploadView();
+ await closeWinscopeSnackBarIfNeeded();
+
+ await clickWinscopeViewTracesButton();
+ await checkWinscopeRenderedSurfaceFlingerView();
+ await checkWinscopeRenderedAllViewTabs();
+ await checkWinscopeAppliedTimestampInBugreportMessage();
+
+ await sendTimestampToWinscope();
+ await checkWinscopeReceivedTimestamp();
+
+ await changeTimestampInWinscope();
+ await checkRemoteToolReceivedTimestamp();
+ });
+
+ const checkServerIsUp = async (name: string, url: string) => {
+ try {
+ await browser.get(url);
+ } catch (error) {
+ fail(`${name} server (${url}) looks down. Did you start it?`);
+ }
+ };
+
+ const openWinscopeTabFromRemoteTool = async () => {
+ await browser.switchTo().window(await getWindowHandleRemoteToolMock());
+ const buttonElement = element(by.css('.button-open-winscope'));
+ await buttonElement.click();
+ };
+
+ const sendBugreportToWinscope = async () => {
+ await browser.switchTo().window(await getWindowHandleRemoteToolMock());
+ const inputFileElement = element(by.css('.button-upload-bugreport'));
+ await inputFileElement.sendKeys(
+ E2eTestUtils.getFixturePath('bugreports/bugreport_stripped.zip')
+ );
+ };
+
+ const checkWinscopeRendersUploadView = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ const isPresent = await element(by.css('.uploaded-files')).isPresent();
+ expect(isPresent).toBeTruthy();
+ };
+
+ const clickWinscopeViewTracesButton = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ await E2eTestUtils.clickViewTracesButton();
+ };
+
+ const closeWinscopeSnackBarIfNeeded = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ };
+
+ const waitWinscopeTabIsOpen = async () => {
+ await browser.wait(
+ async () => {
+ const handles = await browser.getAllWindowHandles();
+ return handles.length >= 2;
+ },
+ 20000,
+ 'The Winscope tab did not open'
+ );
+ };
+
+ const checkWinscopeRenderedSurfaceFlingerView = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ const viewerPresent = await element(by.css('viewer-surface-flinger')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ };
+
+ const checkWinscopeRenderedAllViewTabs = async () => {
+ const tabParagraphs = await element.all(by.css('.tabs-navigation-bar a p'));
+
+ const actualTabParagraphs = await Promise.all(
+ (tabParagraphs as ElementFinder[]).map(async (paragraph) => await paragraph.getText())
+ );
+
+ const expectedTabParagraphs = [
+ 'Input Method Clients',
+ 'Input Method Manager Service',
+ 'Input Method Service',
+ 'ProtoLog',
+ 'Surface Flinger',
+ 'Transactions',
+ 'Transitions',
+ 'Window Manager',
+ ];
+
+ expect(actualTabParagraphs.sort()).toEqual(expectedTabParagraphs.sort());
+ };
+
+ const checkWinscopeAppliedTimestampInBugreportMessage = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ const inputElement = element(by.css('input[name="nsTimeInput"]'));
+ const valueWithNsSuffix = await inputElement.getAttribute('value');
+ expect(valueWithNsSuffix).toEqual(TIMESTAMP_IN_BUGREPORT_MESSAGE + ' ns');
+ };
+
+ const sendTimestampToWinscope = async () => {
+ await browser.switchTo().window(await getWindowHandleRemoteToolMock());
+ const inputElement = element(by.css('.input-timestamp'));
+ await inputElement.sendKeys(TIMESTAMP_FROM_REMOTE_TOOL_TO_WINSCOPE);
+ const buttonElement = element(by.css('.button-send-timestamp'));
+ await buttonElement.click();
+ };
+
+ const checkWinscopeReceivedTimestamp = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ const inputElement = element(by.css('input[name="nsTimeInput"]'));
+ const valueWithNsSuffix = await inputElement.getAttribute('value');
+ expect(valueWithNsSuffix).toEqual(TIMESTAMP_FROM_REMOTE_TOOL_TO_WINSCOPE + ' ns');
+ };
+
+ const changeTimestampInWinscope = async () => {
+ await browser.switchTo().window(await getWindowHandleWinscope());
+ const inputElement = element(by.css('input[name="nsTimeInput"]'));
+ const inputStringStep1 = TIMESTAMP_FROM_WINSCOPE_TO_REMOTE_TOOL.slice(0, -1);
+ const inputStringStep2 = TIMESTAMP_FROM_WINSCOPE_TO_REMOTE_TOOL.slice(-1) + '\r\n';
+ const script = `document.querySelector("input[name=\\"nsTimeInput\\"]").value = "${inputStringStep1}"`;
+ await browser.executeScript(script);
+ await inputElement.sendKeys(inputStringStep2);
+ };
+
+ const checkRemoteToolReceivedTimestamp = async () => {
+ await browser.switchTo().window(await getWindowHandleRemoteToolMock());
+ const paragraphElement = element(by.css('.paragraph-received-timestamp'));
+ const value = await paragraphElement.getText();
+ expect(value).toEqual(TIMESTAMP_FROM_WINSCOPE_TO_REMOTE_TOOL);
+ };
+
+ const getWindowHandleRemoteToolMock = async (): Promise<string> => {
+ const handles = await browser.getAllWindowHandles();
+ expect(handles.length).toBeGreaterThan(0);
+ return handles[0];
+ };
+
+ const getWindowHandleWinscope = async (): Promise<string> => {
+ const handles = await browser.getAllWindowHandles();
+ expect(handles.length).toEqual(2);
+ return handles[1];
+ };
+});
diff --git a/tools/winscope/src/test/e2e/tsconfig.json b/tools/winscope/src/test/e2e/tsconfig.json
new file mode 100644
index 0000000..3da5863
--- /dev/null
+++ b/tools/winscope/src/test/e2e/tsconfig.json
@@ -0,0 +1,30 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "outDir": "../../../dist/e2e_test",
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "allowSyntheticDefaultImports": true,
+ "sourceMap": true,
+ "declaration": false,
+ "downlevelIteration": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "target": "es6",
+ "module": "commonjs",
+ "resolveJsonModule": true,
+ },
+ "include": ["."],
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false,
+ "strictInjectionParameters": true,
+ "strictInputAccessModifiers": true,
+ "strictTemplates": true
+ }
+}
diff --git a/tools/winscope/src/test/e2e/upload_traces_test.ts b/tools/winscope/src/test/e2e/upload_traces_test.ts
new file mode 100644
index 0000000..72554a3
--- /dev/null
+++ b/tools/winscope/src/test/e2e/upload_traces_test.ts
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Upload traces', () => {
+ const DEFAULT_TIMEOUT_MS = 15000;
+
+ beforeAll(async () => {
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = DEFAULT_TIMEOUT_MS;
+ await browser.manage().timeouts().implicitlyWait(DEFAULT_TIMEOUT_MS);
+ });
+
+ beforeEach(async () => {
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ });
+
+ it('can process bugreport', async () => {
+ await E2eTestUtils.uploadFixture('bugreports/bugreport_stripped.zip');
+ await checkHasLoadedTraces();
+ expect(await areMessagesEmitted()).toBeTruthy();
+ await checkEmitsUnsupportedFileFormatMessages();
+ await checkEmitsOverriddenTracesMessages();
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+ await checkRendersSurfaceFlingerView();
+ });
+
+ it("doesn't emit messages for valid trace file", async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb');
+ expect(await areMessagesEmitted()).toBeFalsy();
+ });
+
+ const checkHasLoadedTraces = async () => {
+ const text = await element(by.css('.uploaded-files')).getText();
+ expect(text).toContain('ProtoLog');
+ expect(text).toContain('IME Service');
+ expect(text).toContain('IME Manager Service');
+ expect(text).toContain('Window Manager');
+ expect(text).toContain('Surface Flinger');
+ expect(text).toContain('IME Clients');
+ expect(text).toContain('Transactions');
+ expect(text).toContain('Transitions');
+
+ expect(text).toContain('wm_log.winscope');
+ expect(text).toContain('ime_trace_service.winscope');
+ expect(text).toContain('ime_trace_managerservice.winscope');
+ expect(text).toContain('wm_trace.winscope');
+ expect(text).toContain('layers_trace_from_transactions.winscope');
+ expect(text).toContain('ime_trace_clients.winscope');
+ expect(text).toContain('transactions_trace.winscope');
+ expect(text).toContain('wm_transition_trace.winscope');
+ expect(text).toContain('shell_transition_trace.winscope');
+ };
+
+ const checkEmitsUnsupportedFileFormatMessages = async () => {
+ const text = await element(by.css('snack-bar')).getText();
+ expect(text).toContain('unsupported file format');
+ };
+
+ const checkEmitsOverriddenTracesMessages = async () => {
+ const text = await element(by.css('snack-bar')).getText();
+ expect(text).toContain('overridden by another trace');
+ };
+
+ const areMessagesEmitted = async (): Promise<boolean> => {
+ // Messages are emitted quickly. There is no Need to wait for the entire
+ // default timeout to understand whether the messages where emitted or not.
+ await browser.manage().timeouts().implicitlyWait(1000);
+ const emitted = await element(by.css('snack-bar')).isPresent();
+ await browser.manage().timeouts().implicitlyWait(DEFAULT_TIMEOUT_MS);
+ return emitted;
+ };
+
+ const checkRendersSurfaceFlingerView = async () => {
+ const viewerPresent = await element(by.css('viewer-surface-flinger')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ };
+});
diff --git a/tools/winscope/src/test/e2e/utils.ts b/tools/winscope/src/test/e2e/utils.ts
new file mode 100644
index 0000000..738aae0
--- /dev/null
+++ b/tools/winscope/src/test/e2e/utils.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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 * as path from 'path';
+import {by, element} from 'protractor';
+import {CommonTestUtils} from '../common/utils';
+
+class E2eTestUtils extends CommonTestUtils {
+ static getProductionIndexHtmlPath(): string {
+ return path.join(CommonTestUtils.getProjectRootPath(), 'dist/prod/index.html');
+ }
+
+ static async uploadFixture(...paths: string[]) {
+ const inputFile = element(by.css('input[type="file"]'));
+
+ // Uploading multiple files is not properly supported but
+ // chrome handles file paths joined with new lines
+ await inputFile.sendKeys(paths.map((it) => E2eTestUtils.getFixturePath(it)).join('\n'));
+ }
+
+ static async clickViewTracesButton() {
+ const button = element(by.css('.load-btn'));
+ await button.click();
+ }
+
+ static async closeSnackBarIfNeeded() {
+ const closeButton = element(by.css('.snack-bar-action'));
+ const isPresent = await closeButton.isPresent();
+ if (isPresent) {
+ await closeButton.click();
+ }
+ }
+}
+
+export {E2eTestUtils};
diff --git a/tools/winscope/src/test/e2e/viewer_input_method_clients_test.ts b/tools/winscope/src/test/e2e/viewer_input_method_clients_test.ts
new file mode 100644
index 0000000..1939d87
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_input_method_clients_test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer InputMethodClients', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/InputMethodClients.pb');
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const viewerPresent = await element(by.css('viewer-input-method')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_input_method_manager_service_test.ts b/tools/winscope/src/test/e2e/viewer_input_method_manager_service_test.ts
new file mode 100644
index 0000000..5114a9b
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_input_method_manager_service_test.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer InputMethodManagerService', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture(
+ 'traces/elapsed_and_real_timestamp/InputMethodManagerService.pb'
+ );
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const viewerPresent = await element(by.css('viewer-input-method')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_input_method_service_test.ts b/tools/winscope/src/test/e2e/viewer_input_method_service_test.ts
new file mode 100644
index 0000000..6743845
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_input_method_service_test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer InputMethodService', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/InputMethodService.pb');
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const viewerPresent = await element(by.css('viewer-input-method')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_protolog_test.ts b/tools/winscope/src/test/e2e/viewer_protolog_test.ts
new file mode 100644
index 0000000..6edaaeb
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_protolog_test.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer ProtoLog', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/ProtoLog.pb');
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const isViewerRendered = await element(by.css('viewer-protolog')).isPresent();
+ expect(isViewerRendered).toBeTruthy();
+
+ const isFirstMessageRendered = await element(
+ by.css('viewer-protolog .scroll-messages .message')
+ ).isPresent();
+ expect(isFirstMessageRendered).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_screen_recording_test.ts b/tools/winscope/src/test/e2e/viewer_screen_recording_test.ts
new file mode 100644
index 0000000..d71fdc8
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_screen_recording_test.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer ScreenRecording', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ });
+
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture(
+ 'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4'
+ );
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const viewer = element(by.css('viewer-screen-recording'));
+ expect(await viewer.isPresent()).toBeTruthy();
+
+ const video = element(by.css('viewer-screen-recording video'));
+ expect(await video.isPresent()).toBeTruthy();
+ expect(await video.getAttribute('src')).toContain('blob:');
+ expect(await video.getAttribute('currentTime')).toBeCloseTo(0, 0.001);
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_surface_flinger_test.ts b/tools/winscope/src/test/e2e/viewer_surface_flinger_test.ts
new file mode 100644
index 0000000..788b739
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_surface_flinger_test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer SurfaceFlinger', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb');
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const viewerPresent = await element(by.css('viewer-surface-flinger')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_transactions_test.ts b/tools/winscope/src/test/e2e/viewer_transactions_test.ts
new file mode 100644
index 0000000..3ed07eb
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_transactions_test.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer Transactions', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/Transactions.pb');
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const isViewerRendered = await element(by.css('viewer-transactions')).isPresent();
+ expect(isViewerRendered).toBeTruthy();
+
+ const isFirstEntryRendered = await element(
+ by.css('viewer-transactions .scroll .entry')
+ ).isPresent();
+ expect(isFirstEntryRendered).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_transitions_test.ts b/tools/winscope/src/test/e2e/viewer_transitions_test.ts
new file mode 100644
index 0000000..1161c5f
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_transitions_test.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer Transitions', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ });
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture(
+ 'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
+ 'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
+ );
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const isViewerRendered = await element(by.css('viewer-transitions')).isPresent();
+ expect(isViewerRendered).toBeTruthy();
+
+ const isFirstEntryRendered = await element(
+ by.css('viewer-transitions .scroll .entry')
+ ).isPresent();
+ expect(isFirstEntryRendered).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/viewer_window_manager_test.ts b/tools/winscope/src/test/e2e/viewer_window_manager_test.ts
new file mode 100644
index 0000000..3f20cdd
--- /dev/null
+++ b/tools/winscope/src/test/e2e/viewer_window_manager_test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('Viewer WindowManager', () => {
+ beforeAll(async () => {
+ browser.manage().timeouts().implicitlyWait(1000);
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('processes trace and renders view', async () => {
+ await E2eTestUtils.uploadFixture('traces/elapsed_and_real_timestamp/WindowManager.pb');
+ await E2eTestUtils.closeSnackBarIfNeeded();
+ await E2eTestUtils.clickViewTracesButton();
+
+ const viewerPresent = await element(by.css('viewer-window-manager')).isPresent();
+ expect(viewerPresent).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/test/e2e/winscope_test.ts b/tools/winscope/src/test/e2e/winscope_test.ts
new file mode 100644
index 0000000..4d60e44
--- /dev/null
+++ b/tools/winscope/src/test/e2e/winscope_test.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 {browser, by, element} from 'protractor';
+import {E2eTestUtils} from './utils';
+
+describe('winscope', () => {
+ beforeAll(() => {
+ browser.get('file://' + E2eTestUtils.getProductionIndexHtmlPath());
+ }),
+ it('has title', () => {
+ const title = element(by.css('.app-title'));
+ expect(title.getText()).toContain('Winscope');
+ });
+});
diff --git a/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt b/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt
new file mode 100644
index 0000000..c02add5
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt
@@ -0,0 +1,6 @@
+========================================================
+== dumpstate: 2023-05-30 14:33:48
+========================================================
+
+# ORIGINAL CONTENTS REMOVED TO AVOID INFORMATION LEAKS
+
diff --git a/tools/winscope/src/test/fixtures/bugreports/bugreport_stripped.zip b/tools/winscope/src/test/fixtures/bugreports/bugreport_stripped.zip
new file mode 100644
index 0000000..c91a595
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/bugreports/bugreport_stripped.zip
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/bugreports/main_entry.txt b/tools/winscope/src/test/fixtures/bugreports/main_entry.txt
new file mode 100644
index 0000000..3399205
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/bugreports/main_entry.txt
@@ -0,0 +1 @@
+bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt
diff --git a/tools/winscope/src/test/fixtures/traces/dump_WindowManager.pb b/tools/winscope/src/test/fixtures/traces/dump_WindowManager.pb
new file mode 100644
index 0000000..ab7fe9f
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/dump_WindowManager.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/Accessibility.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/Accessibility.pb
new file mode 100644
index 0000000..6780f22
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/Accessibility.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodClients.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodClients.pb
new file mode 100644
index 0000000..b70ea7f
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodClients.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodManagerService.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodManagerService.pb
new file mode 100644
index 0000000..cf5f0ca
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodManagerService.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodService.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodService.pb
new file mode 100644
index 0000000..4b7c330
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/InputMethodService.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/ProtoLog.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/ProtoLog.pb
new file mode 100644
index 0000000..cc8777b
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/ProtoLog.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger.pb
new file mode 100644
index 0000000..d26f5cc
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger_multidisplay.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger_multidisplay.pb
new file mode 100644
index 0000000..07fa2c7
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/SurfaceFlinger_multidisplay.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/Transactions.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/Transactions.pb
new file mode 100644
index 0000000..3ffe81a
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/Transactions.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/WindowManager.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/WindowManager.pb
new file mode 100644
index 0000000..7749b4b
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/WindowManager.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb
new file mode 100644
index 0000000..b5b884f
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4 b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4
new file mode 100644
index 0000000..a6c43d6
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/shell_transition_trace.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/shell_transition_trace.pb
new file mode 100644
index 0000000..e49d7e1
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/shell_transition_trace.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/wm_transition_trace.pb b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/wm_transition_trace.pb
new file mode 100644
index 0000000..f721d52
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/wm_transition_trace.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Accessibility.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Accessibility.pb
new file mode 100644
index 0000000..5414994
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Accessibility.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodClients.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodClients.pb
new file mode 100644
index 0000000..a842c21
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodClients.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodManagerService.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodManagerService.pb
new file mode 100644
index 0000000..7129f95
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodManagerService.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodService.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodService.pb
new file mode 100644
index 0000000..51fa2f9
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/InputMethodService.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/SurfaceFlinger.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/SurfaceFlinger.pb
new file mode 100644
index 0000000..cf2d1a8
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/SurfaceFlinger.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transactions.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transactions.pb
new file mode 100644
index 0000000..c06ec6b
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transactions.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb
new file mode 100644
index 0000000..c639db4
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb
@@ -0,0 +1,9 @@
+ TRNTRACE@¶°àη°àÎ)8B
+áûÆØB .íöÚ ÄÎÍ6(¿ÔÙ60¢Û6@°±àα±àÎ*8B .íöÚB
+áûÆØ ºà6(ú¥èì60©«á¦6@´àÎ
´àÎ+8B
+¥×ª-B .íöÚ °§¥6(¬³¥60¶úÒê¦6@î´àÎï´àÎ,8B .íöÚB
+¥×ª- ©ëЫ6(â«60®6@·àηàÎ-8B
+Þá»ðvB .íöÚ É´®Ê´6(×ùô×´60õô
Ò¶6A¸àθàÎ.8B
+í¥©õoB
+ßÈö×r ѸȻ6(ïô»60¨©ì½6@±¸àβ¸àÎ/8B .íöÚB
+Þá»ðv ðüùñ½6(À ¾60ý꼪À6
\ No newline at end of file
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/WindowManager.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/WindowManager.pb
new file mode 100644
index 0000000..8c236bb
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/WindowManager.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/dump_SurfaceFlinger.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/dump_SurfaceFlinger.pb
new file mode 100644
index 0000000..46ee306
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/dump_SurfaceFlinger.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/screen_recording.mp4 b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/screen_recording.mp4
new file mode 100644
index 0000000..9cdedfe
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/screen_recording.mp4
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/empty.pb b/tools/winscope/src/test/fixtures/traces/empty.pb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/empty.pb
diff --git a/tools/winscope/src/test/fixtures/traces/eventlog.winscope b/tools/winscope/src/test/fixtures/traces/eventlog.winscope
new file mode 100644
index 0000000..dfd933d
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/eventlog.winscope
@@ -0,0 +1,187 @@
+EventLog
+
+--------- beginning of events
+ 1681207047.981157174 1000 1654 1821 I input_interaction: Interaction with: 454ee48 com.google.android.gm/com.google.android.gm.welcome.WelcomeTourActivity (server), [Gesture Monitor] swipe-up (server), [Gesture Monitor] edge-swipe (server), PointerEventDispatcher0 (server),
+ 1681207047.991161039 1000 1654 6209 I input_cancel: [454ee48 com.google.android.gm/com.google.android.gm.welcome.WelcomeTourActivity (server),reason=input channel stole pointer stream]
+ 1681207047.991310494 1000 1654 6209 I input_cancel: [[Gesture Monitor] edge-swipe (server),reason=input channel stole pointer stream]
+ 1681207047.991408313 10212 14576 14576 I view_enqueue_input_event: [Motion - Cancel,com.google.android.gm/com.google.android.gm.welcome.WelcomeTourActivity]
+ 1681207047.993790312 1000 1654 1821 I input_interaction: Interaction with: [Gesture Monitor] swipe-up (server), PointerEventDispatcher0 (server),
+ 1681207047.999042631 1000 1654 1695 I wm_task_moved: [1,1,0,1,5]
+ 1681207047.999255237 1000 1654 1695 I wm_task_moved: [101,1,0,1,2147483647]
+ 1681207047.999270089 1000 1654 1695 I wm_task_to_front: [0,101,0]
+ 1681207047.999394763 10212 14576 14576 I wm_on_top_resumed_lost_called: [32607344,com.google.android.gm.welcome.WelcomeTourActivity,topStateChangedWhenResumed]
+ 1681207047.999401884 1000 1654 1695 I wm_focused_root_task: [0,0,1,-1,startExistingRecents]
+ 1681207047.999987984 1000 1654 1695 I wm_set_resumed_activity: [0,com.google.android.apps.nexuslauncher/.NexusLauncherActivity,resumeTopActivity - onActivityStateChanged]
+ 1681207048.000370959 1000 1654 1695 I wm_resume_activity: [0,225733613,101,com.google.android.apps.nexuslauncher/.NexusLauncherActivity]
+ 1681207048.007536649 10227 2806 2806 I wm_on_restart_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,performRestart,0]
+ 1681207048.010586902 10227 2806 2806 I wm_on_start_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,handleStartActivity,3]
+ 1681207048.010844674 10227 2806 2806 I wm_on_resume_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,RESUME_ACTIVITY,0]
+ 1681207048.011009346 10227 2806 2806 I wm_on_top_resumed_gained_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,topStateChangedWhenResumed]
+ 1681207048.025492501 10227 2806 3604 I jank_cuj_events_begin_request: [11,1681207048025446000,2661012770462,2661012770503,]
+ 1681207048.025580310 10227 2806 3604 I jank_cuj_events_begin_request: [9,1681207048025551000,2661012874914,2661012874954,]
+ 1681207048.025596830 10227 2806 3604 I jank_cuj_events_begin_request: [66,1681207048025580000,2661012903966,2661012904007,]
+ 1681207048.026301705 10227 2806 2806 I sysui_latency: [1,53]
+ 1681207048.031300362 1000 1654 1696 I wm_wallpaper_surface: [0,1,Window{268cd60 u0 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}]
+ 1681207048.034518461 1000 1654 1692 I sysui_multi_action: [319,32,322,30,325,2660,757,761,758,9,759,5,806,com.google.android.apps.nexuslauncher,871,com.google.android.apps.nexuslauncher.NexusLauncherActivity,905,0,1320,4,1321,24]
+ 1681207048.037651070 1000 1654 10793 I wm_set_resumed_activity: [0,com.google.android.apps.nexuslauncher/.NexusLauncherActivity,setFocusedTask-alreadyTop]
+ 1681207048.039444812 1000 1654 1696 I input_focus: [Focus request recents_animation_input_consumer,reason=UpdateInputWindows]
+ 1681207048.054928902 1000 1654 1821 I input_focus: [Focus leaving 454ee48 com.google.android.gm/com.google.android.gm.welcome.WelcomeTourActivity (server),reason=Waiting for window because NOT_VISIBLE]
+ 1681207048.055058459 1000 1654 1821 I input_focus: [Focus entering recents_animation_input_consumer (server),reason=Window became focusable. Previous reason: NOT_VISIBLE]
+ 1681207048.055324695 10227 2806 3604 I jank_cuj_events_end_request: [65,1681207048055285000,2661042609858,2661042609980]
+ 1681207048.286107084 10241 2032 2153 I jank_cuj_events_end_request: [55,1681207048285762000,2661273088374,2661273088496]
+ 1681207048.331988147 1000 1654 1692 I sysui_latency: [8,44]
+ 1681207048.643178618 10227 2806 3604 I jank_cuj_events_cancel_request: [11,1681207048642792000,2661630123246,2661630123815]
+ 1681207048.643397652 10227 2806 3604 I jank_cuj_events_cancel_request: [66,1681207048643085000,2661630409297,2661630409541]
+ 1681207048.656586821 10227 2806 3604 I jank_cuj_events_end_request: [11,1681207048656469000,2661643794470,2661643794673]
+ 1681207048.656637521 10227 2806 3604 I jank_cuj_events_end_request: [9,1681207048656499000,2661643822871,2661643822993]
+ 1681207048.656670479 10227 2806 3604 I jank_cuj_events_end_request: [66,1681207048656617000,2661643941035,2661643941239]
+ 1681207048.658915515 1000 1654 3618 I wm_add_to_stopping: [0,32607344,com.google.android.gm/.welcome.WelcomeTourActivity,makeInvisible]
+ 1681207048.659206816 1000 1654 3618 I wm_pause_activity: [0,32607344,com.google.android.gm/.welcome.WelcomeTourActivity,userLeaving=true,finishTransition]
+ 1681207048.663365141 10212 14576 14576 I wm_on_paused_called: [32607344,com.google.android.gm.welcome.WelcomeTourActivity,performPause,3]
+ 1681207048.671711292 1000 1654 1696 I input_focus: [Focus request 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity,reason=UpdateInputWindows]
+ 1681207048.671949939 1000 1654 1821 I input_focus: [Focus leaving recents_animation_input_consumer (server),reason=NOT_VISIBLE]
+ 1681207048.673020292 1000 1654 1692 I sysui_multi_action: [757,803,799,window_time_0,802,4]
+ 1681207048.673024728 1000 1654 1695 I wm_stop_activity: [0,32607344,com.google.android.gm/.welcome.WelcomeTourActivity]
+ 1681207048.687825346 10212 14576 14576 I wm_on_stop_called: [32607344,com.google.android.gm.welcome.WelcomeTourActivity,STOP_ACTIVITY_ITEM,13]
+ 1681207048.702757027 1000 1654 1821 I input_focus: [Focus entering 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server),reason=setFocusedWindow]
+ 1681207048.915948881 1000 1654 1821 I input_interaction: Interaction with: 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server), c61a53b com.android.systemui.wallpapers.ImageWallpaper (server), [Gesture Monitor] swipe-up (server), [Gesture Monitor] edge-swipe (server), PointerEventDispatcher0 (server),
+ 1681207048.916834054 10227 2806 3604 I jank_cuj_events_begin_request: [25,1681207048916587000,2661903912227,2661903912390,]
+ 1681207048.916890125 10227 2806 3604 I jank_cuj_events_begin_request: [67,1681207048916771000,2661904095129,2661904095210,]
+ 1681207048.975377958 10227 2806 3604 I jank_cuj_events_cancel_request: [25,1681207048975284000,2661962609737,2661962609940]
+ 1681207048.975487740 10227 2806 3604 I jank_cuj_events_cancel_request: [67,1681207048975422000,2661962746089,2661962746293]
+ 1681207048.997640003 1000 1654 10793 I wm_task_created: 109
+ 1681207049.002909331 1000 1654 10793 I wm_task_moved: [109,109,0,1,6]
+ 1681207049.002936634 1000 1654 10793 I wm_task_to_front: [0,109,0]
+ 1681207049.003380481 1000 1654 10793 I wm_create_task: [0,109,109,0]
+ 1681207049.003422148 1000 1654 10793 I wm_create_activity: [0,188766343,109,com.google.android.apps.photos/.home.HomeActivity,android.intent.action.MAIN,NULL,NULL,270532608]
+ 1681207049.003610177 1000 1654 10793 I wm_task_moved: [109,109,0,1,6]
+ 1681207049.005225045 1000 1654 10793 I wm_pause_activity: [0,225733613,com.google.android.apps.nexuslauncher/.NexusLauncherActivity,userLeaving=true,pauseBackTasks]
+ 1681207049.008557768 10227 2806 2806 I wm_on_top_resumed_lost_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,topStateChangedWhenResumed]
+ 1681207049.009063790 10227 2806 2806 I wm_on_paused_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,performPause,0]
+ 1681207049.010960844 1000 1654 11103 I wm_add_to_stopping: [0,225733613,com.google.android.apps.nexuslauncher/.NexusLauncherActivity,makeInvisible]
+ 1681207049.011341297 1000 1654 11103 I wm_restart_activity: [0,188766343,109,com.google.android.apps.photos/.home.HomeActivity]
+ 1681207049.012786813 1000 1654 11103 I wm_set_resumed_activity: [0,com.google.android.apps.photos/.home.HomeActivity,minimalResumeActivityLocked - onActivityStateChanged]
+ 1681207049.014562651 1000 1654 1692 I sysui_multi_action: [757,803,799,window_time_0,802,1]
+ 1681207049.016519519 1000 1654 1695 I am_uid_active: 10216
+ 1681207049.016556629 1000 1654 1696 I input_focus: [Requesting to set focus to null window,reason=UpdateInputWindows]
+ 1681207049.021999215 1000 1654 1808 I am_unfreeze: [14176,com.google.android.apps.photos]
+ 1681207049.036460112 10227 2806 3604 I jank_cuj_events_begin_request: [8,1681207049036390000,2662023713985,2662023714066,]
+ 1681207049.037569975 1000 1654 1821 I input_focus: [Focus leaving 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server),reason=Waiting for window because NO_WINDOW]
+ 1681207049.059874012 10216 14176 14176 I wm_on_create_called: [188766343,com.google.android.apps.photos.home.HomeActivity,performCreate,20]
+ 1681207049.061116647 10216 14176 14176 I wm_on_start_called: [188766343,com.google.android.apps.photos.home.HomeActivity,handleStartActivity,1]
+ 1681207049.062957833 10216 14176 14176 I wm_on_resume_called: [188766343,com.google.android.apps.photos.home.HomeActivity,RESUME_ACTIVITY,1]
+ 1681207049.068858834 10216 14176 14176 I wm_on_top_resumed_gained_called: [188766343,com.google.android.apps.photos.home.HomeActivity,topStateChangedWhenResumed]
+ 1681207049.078810901 1000 1654 1696 I input_focus: [Focus request 2c6a168 com.google.android.apps.photos/com.google.android.apps.photos.home.HomeActivity,reason=UpdateInputWindows]
+ 1681207049.088823271 10241 2032 2153 I jank_cuj_events_begin_request: [39,1681207049088775000,2662076099076,2662076099117,]
+ 1681207049.092389962 1000 1654 1692 I sysui_multi_action: [319,44,321,40,322,100,325,2661,757,761,758,8,759,1,806,com.google.android.apps.photos,871,com.google.android.apps.photos.home.HomeActivity,904,com.google.android.apps.nexuslauncher,905,0,1320,4,1321,24]
+ 1681207049.093085315 1000 1654 1692 I wm_activity_launch_time: [0,188766343,com.google.android.apps.photos/.home.HomeActivity,100]
+ 1681207049.102520415 1000 1654 1821 I input_focus: [Focus entering 2c6a168 com.google.android.apps.photos/com.google.android.apps.photos.home.HomeActivity (server),reason=Window became focusable. Previous reason: NO_WINDOW]
+ 1681207049.153166248 1000 1654 1692 I sysui_multi_action: [324,1,757,1090,758,12,806,com.google.android.apps.photos,871,com.google.android.apps.photos.home.HomeActivity,1091,165]
+ 1681207049.454860828 10241 2032 2153 I jank_cuj_events_end_request: [39,1681207049454643000,2662441975093,2662441975297]
+ 1681207049.554655018 10227 2806 3604 I jank_cuj_events_end_request: [8,1681207049554479000,2662541809688,2662541810136]
+ 1681207049.561896025 1000 1654 1695 I wm_stop_activity: [0,225733613,com.google.android.apps.nexuslauncher/.NexusLauncherActivity]
+ 1681207049.574914905 1000 1654 11103 I wm_wallpaper_surface: [0,0,null]
+ 1681207049.579730294 10227 2806 2806 I wm_on_stop_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,STOP_ACTIVITY_ITEM,2]
+ 1681207049.591011300 1000 1654 1808 I am_unfreeze: [9196,com.google.process.gservices]
+ 1681207049.717793608 1000 1654 6209 I am_uid_active: 10167
+ 1681207049.885676706 1000 1654 1821 I input_interaction: Interaction with: 2c6a168 com.google.android.apps.photos/com.google.android.apps.photos.home.HomeActivity (server), [Gesture Monitor] swipe-up (server), [Gesture Monitor] edge-swipe (server), PointerEventDispatcher0 (server),
+ 1681207049.899046172 1000 1654 1695 I wm_task_moved: [1,1,0,1,6]
+ 1681207049.899344186 1000 1654 1695 I wm_task_moved: [101,1,0,1,2147483647]
+ 1681207049.899370431 1000 1654 1695 I wm_task_to_front: [0,101,0]
+ 1681207049.899554717 1000 1654 1695 I wm_focused_root_task: [0,0,1,-1,startExistingRecents]
+ 1681207049.899762643 10216 14176 14176 I wm_on_top_resumed_lost_called: [188766343,com.google.android.apps.photos.home.HomeActivity,topStateChangedWhenResumed]
+ 1681207049.900260487 1000 1654 1695 I wm_set_resumed_activity: [0,com.google.android.apps.nexuslauncher/.NexusLauncherActivity,resumeTopActivity - onActivityStateChanged]
+ 1681207049.900776519 1000 1654 1695 I wm_resume_activity: [0,225733613,101,com.google.android.apps.nexuslauncher/.NexusLauncherActivity]
+ 1681207049.904926177 10227 2806 2806 I wm_on_restart_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,performRestart,0]
+ 1681207049.907580270 10227 2806 2806 I wm_on_start_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,handleStartActivity,2]
+ 1681207049.907795033 10227 2806 2806 I wm_on_resume_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,RESUME_ACTIVITY,0]
+ 1681207049.908100493 10227 2806 2806 I wm_on_top_resumed_gained_called: [225733613,com.google.android.apps.nexuslauncher.NexusLauncherActivity,topStateChangedWhenResumed]
+ 1681207049.913157134 1000 1654 2023 I input_cancel: [2c6a168 com.google.android.apps.photos/com.google.android.apps.photos.home.HomeActivity (server),reason=input channel stole pointer stream]
+ 1681207049.913244862 1000 1654 2023 I input_cancel: [[Gesture Monitor] edge-swipe (server),reason=input channel stole pointer stream]
+ 1681207049.913293649 10216 14176 14176 I view_enqueue_input_event: [Motion - Cancel,com.google.android.apps.photos/com.google.android.apps.photos.home.HomeActivity]
+ 1681207049.917973988 1000 1654 1821 I input_interaction: Interaction with: [Gesture Monitor] swipe-up (server), PointerEventDispatcher0 (server),
+ 1681207049.924828520 10227 2806 2806 I sysui_latency: [1,42]
+ 1681207049.924956287 10227 2806 3604 I jank_cuj_events_begin_request: [11,1681207049924893000,2662912216671,2662912216833,]
+ 1681207049.924998890 10227 2806 3604 I jank_cuj_events_begin_request: [9,1681207049924982000,2662912305375,2662912305416,]
+ 1681207049.925024484 10227 2806 3604 I jank_cuj_events_begin_request: [66,1681207049925003000,2662912326453,2662912326493,]
+ 1681207049.930600981 1000 1654 1696 I wm_wallpaper_surface: [0,1,Window{268cd60 u0 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}]
+ 1681207049.931878773 1000 1654 1692 I sysui_multi_action: [319,31,322,29,325,2662,757,761,758,9,759,5,806,com.google.android.apps.nexuslauncher,871,com.google.android.apps.nexuslauncher.NexusLauncherActivity,905,0,1320,4,1321,24]
+ 1681207049.935776315 1000 1654 11103 I wm_set_resumed_activity: [0,com.google.android.apps.nexuslauncher/.NexusLauncherActivity,setFocusedTask-alreadyTop]
+ 1681207049.940226510 1000 1654 1696 I input_focus: [Focus request recents_animation_input_consumer,reason=UpdateInputWindows]
+ 1681207049.952110259 1000 1654 1821 I input_focus: [Focus leaving 2c6a168 com.google.android.apps.photos/com.google.android.apps.photos.home.HomeActivity (server),reason=Waiting for window because NOT_VISIBLE]
+ 1681207049.952171009 1000 1654 1821 I input_focus: [Focus entering recents_animation_input_consumer (server),reason=Window became focusable. Previous reason: NOT_VISIBLE]
+ 1681207049.965379790 10227 2806 3604 I jank_cuj_events_end_request: [65,1681207049965303000,2662952628210,2662952628414]
+ 1681207050.167619333 10241 2032 2153 I jank_cuj_events_end_request: [55,1681207050167531000,2663154859981,2663154860225]
+ 1681207050.230896758 1000 1654 1692 I sysui_latency: [8,40]
+ 1681207050.555624012 10227 2806 3604 I jank_cuj_events_cancel_request: [11,1681207050555494000,2663542820390,2663542820634]
+ 1681207050.555731678 10227 2806 3604 I jank_cuj_events_cancel_request: [66,1681207050555695000,2663543019527,2663543019812]
+ 1681207050.570117298 10227 2806 3604 I jank_cuj_events_end_request: [11,1681207050570020000,2663557348792,2663557349239]
+ 1681207050.570168487 10227 2806 3604 I jank_cuj_events_end_request: [9,1681207050570074000,2663557398434,2663557398556]
+ 1681207050.570319081 10227 2806 3604 I jank_cuj_events_end_request: [66,1681207050570285000,2663557609168,2663557609290]
+ 1681207050.573875396 1000 1654 11103 I wm_add_to_stopping: [0,188766343,com.google.android.apps.photos/.home.HomeActivity,makeInvisible]
+ 1681207050.574604929 1000 1654 11103 I wm_pause_activity: [0,188766343,com.google.android.apps.photos/.home.HomeActivity,userLeaving=true,finishTransition]
+ 1681207050.584755767 1000 1654 1696 I input_focus: [Focus request 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity,reason=UpdateInputWindows]
+ 1681207050.586915028 1000 1654 1821 I input_focus: [Focus leaving recents_animation_input_consumer (server),reason=NOT_VISIBLE]
+ 1681207050.597062896 10216 14176 14176 I wm_on_paused_called: [188766343,com.google.android.apps.photos.home.HomeActivity,performPause,21]
+ 1681207050.598423329 1000 1654 1692 I sysui_multi_action: [757,803,799,window_time_0,802,1]
+ 1681207050.598497710 1000 1654 1695 I wm_stop_activity: [0,188766343,com.google.android.apps.photos/.home.HomeActivity]
+ 1681207050.619719064 1000 1654 1821 I input_focus: [Focus entering 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server),reason=setFocusedWindow]
+ 1681207050.648848825 1000 1654 1821 I input_interaction: Interaction with: 956d30 StatusBar (server), [Gesture Monitor] swipe-up (server), [Gesture Monitor] edge-swipe (server), PointerEventDispatcher0 (server),
+ 1681207050.650068511 10241 2032 2032 I sysui_multi_action: [757,803,799,panel_open,802,1]
+ 1681207050.650466827 10241 2032 2153 I jank_cuj_events_begin_request: [0,1681207050650381000,2663637707434,2663637707678,Expand]
+ 1681207050.651434600 10241 2032 2032 I sysui_multi_action: [757,1328,758,4,1326,25,1327,4,1329,0]
+ 1681207050.702902170 10216 14176 14176 I wm_on_stop_called: [188766343,com.google.android.apps.photos.home.HomeActivity,STOP_ACTIVITY_ITEM,64]
+ 1681207050.704760650 1000 1654 6209 I sysui_multi_action: [757,127,758,1]
+ 1681207050.704790435 1000 1654 6209 I sysui_multi_action: [757,804,799,note_load,801,5,802,1]
+ 1681207050.704877186 1000 1654 6209 I notification_panel_revealed: 5
+ 1681207050.716025827 1000 1654 1696 I input_focus: [Focus request fd59b56 NotificationShade,reason=UpdateInputWindows]
+ 1681207050.720782297 1000 1654 1821 I input_focus: [Focus leaving 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server),reason=Waiting for window because NOT_VISIBLE]
+ 1681207050.723768828 1000 1654 3830 I sysui_multi_action: [757,128,758,1,793,2642524,794,0,795,2642524,796,26,798,1,806,android,857,DEVELOPER_IMPORTANT,858,4,946,ranker_group,947,0,1395,5,1500,796026,1688,1]
+ 1681207050.723832427 1000 1654 3830 I sysui_multi_action: [757,1501,758,1,793,2642524,794,0,795,2642524,796,26,806,android,857,DEVELOPER_IMPORTANT,858,4,946,ranker_group,947,0,1500,0,1688,1]
+ 1681207050.723867543 1000 1654 3830 I sysui_multi_action: [757,804,799,note_interruptive,801,0,802,1]
+ 1681207050.723889556 1000 1654 3830 I sysui_multi_action: [757,804,799,note_freshness,801,2642524,802,1]
+ 1681207050.723922474 1000 1654 3830 I notification_visibility: [-1|android|26|null|1000,1,2642524,2642524,0,1]
+ 1681207050.732759185 10241 2032 2153 I jank_cuj_events_begin_request: [0,1681207050732669000,2663719994178,2663719994300,Collapse]
+ 1681207050.736113555 1000 1654 1821 I input_focus: [Focus entering fd59b56 NotificationShade (server),reason=Window became focusable. Previous reason: NOT_VISIBLE]
+ 1681207051.227287302 1000 1654 3830 I sysui_multi_action: [757,128,758,1,793,2643147,794,0,795,2643147,796,54,798,2,806,android,857,DEVELOPER,858,2,946,ranker_group,947,0,1395,5,1500,796027,1688,1]
+ 1681207051.227472808 1000 1654 3830 I sysui_multi_action: [757,1501,758,1,793,2643147,794,0,795,2643147,796,54,806,android,857,DEVELOPER,858,2,946,ranker_group,947,0,1500,0,1688,1]
+ 1681207051.227528187 1000 1654 3830 I sysui_multi_action: [757,804,799,note_interruptive,801,0,802,1]
+ 1681207051.227574452 1000 1654 3830 I sysui_multi_action: [757,804,799,note_freshness,801,2643147,802,1]
+ 1681207051.227630930 1000 1654 3830 I notification_visibility: [-1|android|54|null|1000,1,2643147,2643147,0,2]
+ 1681207051.228475250 1000 1654 3830 I sysui_multi_action: [757,128,758,1,793,2632336,794,0,795,2632336,796,1,798,4,806,com.google.euiccpixel,857,ota_update,858,4,947,0,1395,5,1500,796027,1688,1,1745,2098068913]
+ 1681207051.228584014 1000 1654 3830 I sysui_multi_action: [757,1501,758,1,793,2632336,794,0,795,2632336,796,1,806,com.google.euiccpixel,857,ota_update,858,4,947,0,1500,0,1688,1,1745,2098068913]
+ 1681207051.228650136 1000 1654 3830 I sysui_multi_action: [757,804,799,note_interruptive,801,0,802,1]
+ 1681207051.228761627 1000 1654 3830 I sysui_multi_action: [757,804,799,note_freshness,801,2632336,802,1]
+ 1681207051.228805531 1000 1654 3830 I notification_visibility: [0|com.google.euiccpixel|1|null|10238,1,2632336,2632336,0,4]
+ 1681207051.229276885 1000 1654 3830 I sysui_multi_action: [757,128,758,1,793,2643141,794,0,795,2643141,796,2147483647,797,ranker_group,798,0,806,android,857,USB,858,1,946,ranker_group,947,1,1395,5,1500,2643141,1688,1]
+ 1681207051.229347116 1000 1654 3830 I sysui_multi_action: [757,804,799,note_freshness,801,2643141,802,1]
+ 1681207051.229412668 1000 1654 3830 I notification_visibility: [-1|android|2147483647|ranker_group|1000|ranker_group,1,2643141,2643141,0,0]
+ 1681207051.229923410 1000 1654 3830 I sysui_multi_action: [757,128,758,1,793,429394689,794,0,795,429394689,796,32,798,3,806,android,857,USB,858,1,946,ranker_group,947,0,1395,5,1500,796027,1688,1]
+ 1681207051.230036854 1000 1654 3830 I sysui_multi_action: [757,1501,758,1,793,429394689,794,0,795,429394689,796,32,806,android,857,USB,858,1,946,ranker_group,947,0,1500,0,1688,1]
+ 1681207051.230071929 1000 1654 3830 I sysui_multi_action: [757,804,799,note_interruptive,801,0,802,1]
+ 1681207051.230103708 1000 1654 3830 I sysui_multi_action: [757,804,799,note_freshness,801,429394689,802,1]
+ 1681207051.230129302 1000 1654 3830 I notification_visibility: [-1|android|32|null|1000,1,429394689,429394689,0,3]
+ 1681207051.423202423 10241 2032 2153 I jank_cuj_events_end_request: [0,1681207051423045000,2664410371619,2664410371741]
+ 1681207051.475255930 1000 1654 1821 I input_interaction: Interaction with: fd59b56 NotificationShade (server), [Gesture Monitor] swipe-up (server), [Gesture Monitor] edge-swipe (server), PointerEventDispatcher0 (server),
+ 1681207051.477718251 10241 2032 2153 I jank_cuj_events_begin_request: [5,1681207051477630000,2664464955441,2664464955522,]
+ 1681207051.478089060 10241 2032 2153 I jank_cuj_events_begin_request: [5,1681207051478052000,2664465375729,2664465375810,]
+ 1681207051.478207061 10241 2032 2153 I jank_cuj_events_begin_request: [0,1681207051478167000,2664465490963,2664465491045,Collapse]
+ 1681207051.546663035 10241 2032 2153 I jank_cuj_events_end_request: [5,1681207051546596000,2664533922034,2664533922197]
+ 1681207051.547048736 10241 2032 2153 I jank_cuj_events_begin_request: [0,1681207051547013000,2664534337358,2664534337480,Collapse]
+ 1681207051.697259592 1000 1654 1694 I commit_sys_config_file: [jobs,2]
+ 1681207051.736497629 1000 1654 11103 I sysui_multi_action: [757,128,758,2,793,2643656,794,509,795,2643656,796,54,798,2,806,android,857,DEVELOPER,858,2,946,ranker_group,947,0,1395,5,1500,509,1688,1]
+ 1681207051.736568552 1000 1654 11103 I notification_visibility: [-1|android|54|null|1000,0,2643656,2643656,0,2]
+ 1681207051.737020009 1000 1654 11103 I sysui_multi_action: [757,128,758,2,793,2632844,794,508,795,2632844,796,1,798,4,806,com.google.euiccpixel,857,ota_update,858,4,947,0,1395,5,1500,508,1688,1,1745,2098068913]
+ 1681207051.737043528 1000 1654 11103 I notification_visibility: [0|com.google.euiccpixel|1|null|10238,0,2632844,2632844,0,4]
+ 1681207051.737252756 1000 1654 11103 I sysui_multi_action: [757,128,758,2,793,2643649,794,508,795,2643649,796,2147483647,797,ranker_group,798,0,806,android,857,USB,858,1,946,ranker_group,947,1,1395,5,1500,2643649,1688,1]
+ 1681207051.737275909 1000 1654 11103 I notification_visibility: [-1|android|2147483647|ranker_group|1000|ranker_group,0,2643649,2643649,0,0]
+ 1681207051.737472849 1000 1654 11103 I sysui_multi_action: [757,128,758,2,793,429395197,794,508,795,429395197,796,32,798,3,806,android,857,USB,858,1,946,ranker_group,947,0,1395,5,1500,508,1688,1]
+ 1681207051.737495391 1000 1654 11103 I notification_visibility: [-1|android|32|null|1000,0,429395197,429395197,0,3]
+ 1681207051.773215077 1000 1654 11103 I sysui_multi_action: [757,128,758,2,793,2643573,794,1049,795,2643573,796,26,798,1,806,android,857,DEVELOPER_IMPORTANT,858,4,946,ranker_group,947,0,1395,5,1500,1049,1688,1]
+ 1681207051.773321034 1000 1654 11103 I notification_visibility: [-1|android|26|null|1000,0,2643573,2643573,0,1]
+ 1681207051.774938832 10241 2032 2153 I jank_cuj_events_end_request: [0,1681207051774831000,2664762157142,2664762157264]
+ 1681207051.786178131 1000 1654 3830 I sysui_multi_action: [757,127,758,2]
+ 1681207051.786332224 1000 1654 3830 I notification_panel_hidden:
+ 1681207051.786431549 1000 1654 1696 I input_focus: [Focus request 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity,reason=UpdateInputWindows]
+ 1681207051.802442250 1000 1654 1821 I input_focus: [Focus leaving fd59b56 NotificationShade (server),reason=NOT_FOCUSABLE]
+ 1681207051.802530873 1000 1654 1821 I input_focus: [Focus entering 268cd60 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server),reason=setFocusedWindow]
\ No newline at end of file
diff --git a/tools/winscope/src/test/fixtures/traces/ime/InputMethodClients.pb b/tools/winscope/src/test/fixtures/traces/ime/InputMethodClients.pb
new file mode 100644
index 0000000..faa19a5
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/ime/InputMethodClients.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/ime/InputMethodManagerService.pb b/tools/winscope/src/test/fixtures/traces/ime/InputMethodManagerService.pb
new file mode 100644
index 0000000..59d8ae3
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/ime/InputMethodManagerService.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/ime/InputMethodService.pb b/tools/winscope/src/test/fixtures/traces/ime/InputMethodService.pb
new file mode 100644
index 0000000..8ad231f
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/ime/InputMethodService.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/ime/SurfaceFlinger_with_IME.pb b/tools/winscope/src/test/fixtures/traces/ime/SurfaceFlinger_with_IME.pb
new file mode 100644
index 0000000..b6a11bb
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/ime/SurfaceFlinger_with_IME.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/ime/WindowManager_with_IME.pb b/tools/winscope/src/test/fixtures/traces/ime/WindowManager_with_IME.pb
new file mode 100644
index 0000000..a351491
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/ime/WindowManager_with_IME.pb
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/traces/no_entries_InputMethodClients.pb b/tools/winscope/src/test/fixtures/traces/no_entries_InputMethodClients.pb
new file mode 100644
index 0000000..36878b8
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/no_entries_InputMethodClients.pb
@@ -0,0 +1 @@
+ IMCTRACE<&ìÝüÿ
\ No newline at end of file
diff --git a/tools/winscope/src/test/fixtures/traces/winscope.zip b/tools/winscope/src/test/fixtures/traces/winscope.zip
new file mode 100644
index 0000000..7c06dc6
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/traces/winscope.zip
Binary files differ
diff --git a/tools/winscope/src/test/fixtures/winscope_homepage.png b/tools/winscope/src/test/fixtures/winscope_homepage.png
new file mode 100644
index 0000000..67d30af
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/winscope_homepage.png
Binary files differ
diff --git a/tools/winscope/src/test/remote_tool_mock/app_component.ts b/tools/winscope/src/test/remote_tool_mock/app_component.ts
new file mode 100644
index 0000000..8d4b656
--- /dev/null
+++ b/tools/winscope/src/test/remote_tool_mock/app_component.ts
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 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 {ChangeDetectorRef, Component, Inject} from '@angular/core';
+import {FunctionUtils} from 'common/function_utils';
+import {
+ Message,
+ MessageBugReport,
+ MessagePing,
+ MessageTimestamp,
+ MessageType,
+} from 'cross_tool/messages';
+
+@Component({
+ selector: 'app-root',
+ template: `
+ <span class="app-title">Remote Tool Mock (simulates cross-tool protocol)</span>
+
+ <hr />
+ <p>Open Winscope tab</p>
+ <input
+ class="button-open-winscope"
+ type="button"
+ value="Open"
+ (click)="onButtonOpenWinscopeClick()" />
+
+ <hr />
+ <p>Send bugreport</p>
+ <input
+ class="button-upload-bugreport"
+ type="file"
+ value=""
+ (change)="onUploadBugreport($event)" />
+
+ <hr />
+ <p>Send timestamp [ns]</p>
+ <input class="input-timestamp" type="number" id="name" name="name" />
+ <input
+ class="button-send-timestamp"
+ type="button"
+ value="Send"
+ (click)="onButtonSendTimestampClick()" />
+
+ <hr />
+ <p>Received timestamp:</p>
+ <p class="paragraph-received-timestamp"></p>
+ `,
+})
+export class AppComponent {
+ static readonly TARGET = 'http://localhost:8080';
+ static readonly TIMESTAMP_IN_BUGREPORT_MESSAGE = 1670509911000000000n;
+
+ private winscope: Window | null = null;
+ private isWinscopeUp = false;
+ private onMessagePongReceived = FunctionUtils.DO_NOTHING;
+
+ constructor(@Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef) {
+ window.addEventListener('message', (event) => {
+ this.onMessageReceived(event);
+ });
+ }
+
+ async onButtonOpenWinscopeClick() {
+ this.openWinscope();
+ await this.waitWinscopeUp();
+ }
+
+ async onUploadBugreport(event: Event) {
+ const [file, buffer] = await this.readInputFile(event);
+ this.sendBugreport(file, buffer);
+ }
+
+ onButtonSendTimestampClick() {
+ const inputTimestampElement = document.querySelector('.input-timestamp')! as HTMLInputElement;
+ this.sendTimestamp(BigInt(inputTimestampElement.value));
+ }
+
+ private openWinscope() {
+ this.printStatus('OPENING WINSCOPE');
+
+ this.winscope = window.open(AppComponent.TARGET);
+ if (!this.winscope) {
+ throw new Error('Failed to open winscope');
+ }
+
+ this.printStatus('OPENED WINSCOPE');
+ }
+
+ private async waitWinscopeUp() {
+ this.printStatus('WAITING WINSCOPE UP');
+
+ const promise = new Promise<void>((resolve) => {
+ this.onMessagePongReceived = () => {
+ this.isWinscopeUp = true;
+ resolve();
+ };
+ });
+
+ setTimeout(async () => {
+ while (!this.isWinscopeUp) {
+ this.winscope!.postMessage(new MessagePing(), AppComponent.TARGET);
+ await this.sleep(10);
+ }
+ }, 0);
+
+ await promise;
+
+ this.printStatus('DONE WAITING (WINSCOPE IS UP)');
+ }
+
+ private sendBugreport(file: File, buffer: ArrayBuffer) {
+ this.printStatus('SENDING BUGREPORT');
+
+ this.winscope!.postMessage(
+ new MessageBugReport(file, AppComponent.TIMESTAMP_IN_BUGREPORT_MESSAGE),
+ AppComponent.TARGET
+ );
+
+ this.printStatus('SENT BUGREPORT');
+ }
+
+ private sendTimestamp(value: bigint) {
+ this.printStatus('SENDING TIMESTAMP');
+
+ this.winscope!.postMessage(new MessageTimestamp(value), AppComponent.TARGET);
+
+ this.printStatus('SENT TIMESTAMP');
+ }
+
+ private onMessageReceived(event: MessageEvent) {
+ const message = event.data as Message;
+ if (!message.type) {
+ console.log('Cross-tool protocol received unrecognized message:', message);
+ return;
+ }
+
+ switch (message.type) {
+ case MessageType.PING:
+ console.log('Cross-tool protocol received unexpected ping message:', message);
+ break;
+ case MessageType.PONG:
+ this.onMessagePongReceived();
+ break;
+ case MessageType.BUGREPORT:
+ console.log('Cross-tool protocol received unexpected bugreport message:', message);
+ break;
+ case MessageType.TIMESTAMP:
+ console.log('Cross-tool protocol received timestamp message:', message);
+ this.onMessageTimestampReceived(message as MessageTimestamp);
+ break;
+ case MessageType.FILES:
+ console.log('Cross-tool protocol received unexpected files message:', message);
+ break;
+ default:
+ console.log('Cross-tool protocol received unrecognized message:', message);
+ break;
+ }
+ }
+
+ private onMessageTimestampReceived(message: MessageTimestamp) {
+ const paragraph = document.querySelector(
+ '.paragraph-received-timestamp'
+ ) as HTMLParagraphElement;
+ paragraph.textContent = message.timestampNs.toString();
+ this.changeDetectorRef.detectChanges();
+ }
+
+ private printStatus(status: string) {
+ console.log('STATUS: ' + status);
+ }
+
+ private sleep(ms: number): Promise<void> {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ private async readInputFile(event: Event): Promise<[File, ArrayBuffer]> {
+ const files: FileList | null = (event?.target as HTMLInputElement)?.files;
+
+ if (!files || !files[0]) {
+ throw new Error('Failed to read input files');
+ }
+
+ return [files[0], await files[0].arrayBuffer()];
+ }
+}
diff --git a/tools/winscope/src/test/remote_tool_mock/app_module.ts b/tools/winscope/src/test/remote_tool_mock/app_module.ts
new file mode 100644
index 0000000..310370a
--- /dev/null
+++ b/tools/winscope/src/test/remote_tool_mock/app_module.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {NgModule} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {AppComponent} from './app_component';
+
+@NgModule({
+ declarations: [AppComponent],
+ imports: [BrowserModule, CommonModule],
+ bootstrap: [AppComponent],
+})
+class AppModule {}
+
+export {AppModule};
diff --git a/tools/winscope/src/test/remote_tool_mock/index.html b/tools/winscope/src/test/remote_tool_mock/index.html
new file mode 100644
index 0000000..4bc3f4b
--- /dev/null
+++ b/tools/winscope/src/test/remote_tool_mock/index.html
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2022 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.
+-->
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>ABT Mock</title>
+</head>
+<body>
+ <app-root></app-root>
+</body>
+</html>
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/test/remote_tool_mock/main.ts
similarity index 67%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/test/remote_tool_mock/main.ts
index bfe19ce..48e8142 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/test/remote_tool_mock/main.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
+import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
+import {AppModule} from './app_module';
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+platformBrowserDynamic()
+ .bootstrapModule(AppModule)
+ .catch((err) => console.error(err));
diff --git a/tools/winscope/src/test/remote_tool_mock/polyfills.ts b/tools/winscope/src/test/remote_tool_mock/polyfills.ts
new file mode 100644
index 0000000..e4555ed
--- /dev/null
+++ b/tools/winscope/src/test/remote_tool_mock/polyfills.ts
@@ -0,0 +1,52 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes recent versions of Safari, Chrome (including
+ * Opera), Edge on the desktop, and iOS and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/tools/winscope/src/test/remote_tool_mock/webpack.config.js b/tools/winscope/src/test/remote_tool_mock/webpack.config.js
new file mode 100644
index 0000000..e81df56
--- /dev/null
+++ b/tools/winscope/src/test/remote_tool_mock/webpack.config.js
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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 HtmlWebpackPlugin = require('html-webpack-plugin');
+const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
+
+module.exports = {
+ resolve: {
+ extensions: ['.ts', '.js', '.css'],
+ modules: [__dirname + '/../../../node_modules', __dirname + '/../../../src', __dirname],
+ },
+
+ module: {
+ rules: [
+ {
+ test: /\.ts$/,
+ use: ['ts-loader', 'angular2-template-loader'],
+ },
+ {
+ test: /\.html$/,
+ use: ['html-loader'],
+ },
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader'],
+ },
+ {
+ test: /\.s[ac]ss$/i,
+ use: ['style-loader', 'css-loader', 'sass-loader'],
+ },
+ ],
+ },
+
+ mode: 'development',
+
+ entry: {
+ polyfills: __dirname + '/polyfills.ts',
+ app: __dirname + '/main.ts',
+ },
+
+ output: {
+ path: __dirname + '/../../../dist/remote_tool_mock',
+ publicPath: '/',
+ filename: 'js/[name].[hash].js',
+ chunkFilename: 'js/[name].[id].[hash].chunk.js',
+ },
+
+ devtool: 'source-map',
+
+ plugins: [
+ new HtmlWebpackPlugin({
+ template: __dirname + '/index.html',
+ inject: 'body',
+ inlineSource: '.(css|js)$',
+ }),
+ new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
+ ],
+};
diff --git a/tools/winscope/src/test/unit/hierarchy_tree_builder.ts b/tools/winscope/src/test/unit/hierarchy_tree_builder.ts
new file mode 100644
index 0000000..1b81798
--- /dev/null
+++ b/tools/winscope/src/test/unit/hierarchy_tree_builder.ts
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 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 'viewers/common/chip';
+import {HierarchyTreeNode} from 'viewers/common/ui_tree_utils';
+
+class HierarchyTreeBuilder {
+ stableId = '';
+ name = '';
+ kind = '';
+ children: HierarchyTreeNode[] = [];
+ shortName?: string;
+ type?: string;
+ id?: string | number;
+ layerId?: number;
+ displayId?: number;
+ stackId?: number;
+ isVisible?: boolean;
+ isMissing?: boolean;
+ hwcCompositionType?: number;
+ zOrderRelativeOfId?: number;
+ zOrderRelativeOf?: any;
+ zOrderRelativeParentOf?: any;
+ isRootLayer?: boolean;
+ showInFilteredView = true;
+ showInOnlyVisibleView?: boolean;
+ simplifyNames = false;
+ chips: Chip[] = [];
+ diffType?: string;
+ skip?: any;
+
+ setId(id: string | number) {
+ this.id = id;
+ return this;
+ }
+
+ setKind(kind: string) {
+ this.kind = kind;
+ return this;
+ }
+
+ setStableId(stableId: string) {
+ this.stableId = stableId;
+ return this;
+ }
+
+ setName(name: string) {
+ this.name = name;
+ return this;
+ }
+
+ setShortName(shortName: string) {
+ this.shortName = shortName;
+ return this;
+ }
+
+ setChips(chips: Chip[]) {
+ this.chips = chips;
+ return this;
+ }
+
+ setDiffType(diffType: string) {
+ this.diffType = diffType;
+ return this;
+ }
+
+ setChildren(children: HierarchyTreeNode[]) {
+ this.children = children;
+ return this;
+ }
+
+ setDisplayId(displayId: number) {
+ this.displayId = displayId;
+ return this;
+ }
+
+ setLayerId(layerId: number) {
+ this.layerId = layerId;
+ return this;
+ }
+
+ setStackId(stackId: number) {
+ this.stackId = stackId;
+ return this;
+ }
+
+ setIsVisible(isVisible: boolean) {
+ this.isVisible = isVisible;
+ return this;
+ }
+
+ setVisibleView(showInOnlyVisibleView: boolean) {
+ this.showInOnlyVisibleView = showInOnlyVisibleView;
+ return this;
+ }
+
+ setFilteredView(showInFilteredView: boolean) {
+ this.showInFilteredView = showInFilteredView;
+ return this;
+ }
+
+ setSimplifyNames(simplifyNames: boolean) {
+ this.simplifyNames = simplifyNames;
+ return this;
+ }
+
+ build(): HierarchyTreeNode {
+ const node = new HierarchyTreeNode(this.name, this.kind, this.stableId, this.children);
+
+ node.chips = this.chips;
+ node.showInFilteredView = this.showInFilteredView;
+ node.simplifyNames = this.simplifyNames;
+
+ if (this.id) {
+ node.id = this.id;
+ }
+
+ if (this.diffType) {
+ node.diffType = this.diffType;
+ }
+
+ if (this.displayId) {
+ node.displayId = this.displayId;
+ }
+
+ if (this.layerId) {
+ node.layerId = this.layerId;
+ }
+
+ if (this.stackId) {
+ node.stackId = this.stackId;
+ }
+
+ if (this.isVisible) {
+ node.isVisible = this.isVisible;
+ }
+
+ if (this.showInOnlyVisibleView) {
+ node.showInOnlyVisibleView = this.showInOnlyVisibleView;
+ }
+
+ if (this.shortName) {
+ node.shortName = this.shortName;
+ }
+
+ return node;
+ }
+}
+
+export {HierarchyTreeBuilder};
diff --git a/tools/winscope/src/test/unit/layer_builder.ts b/tools/winscope/src/test/unit/layer_builder.ts
new file mode 100644
index 0000000..540a66e
--- /dev/null
+++ b/tools/winscope/src/test/unit/layer_builder.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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 {
+ ActiveBuffer,
+ EMPTY_COLOR,
+ EMPTY_RECT,
+ EMPTY_RECTF,
+ EMPTY_TRANSFORM,
+ Layer,
+ LayerProperties,
+} from 'trace/flickerlib/common';
+
+class LayerBuilder {
+ setFlags(value: number): LayerBuilder {
+ this.flags = value;
+ return this;
+ }
+
+ build(): Layer {
+ const properties = new LayerProperties(
+ null /* visibleRegion */,
+ new ActiveBuffer(0, 0, 0, 0),
+ this.flags,
+ EMPTY_RECTF /* bounds */,
+ EMPTY_COLOR,
+ false /* isOpaque */,
+ 0 /* shadowRadius */,
+ 0 /* cornerRadius */,
+ 'type' /* type */,
+ EMPTY_RECTF /* screenBounds */,
+ EMPTY_TRANSFORM /* transform */,
+ EMPTY_RECTF /* sourceBounds */,
+ 0 /* effectiveScalingMode */,
+ EMPTY_TRANSFORM /* bufferTransform */,
+ 0 /* hwcCompositionType */,
+ EMPTY_RECTF /* hwcCrop */,
+ EMPTY_RECT /* hwcFrame */,
+ 0 /* backgroundBlurRadius */,
+ EMPTY_RECT /* crop */,
+ false /* isRelativeOf */,
+ -1 /* zOrderRelativeOfId */,
+ 0 /* stackId */,
+ EMPTY_TRANSFORM /* requestedTransform */,
+ EMPTY_COLOR /* requestedColor */,
+ EMPTY_RECTF /* cornerRadiusCrop */,
+ EMPTY_TRANSFORM /* inputTransform */,
+ null /* inputRegion */
+ );
+
+ return new Layer(
+ 'name' /* name */,
+ 0 /* id */,
+ -1 /*parentId */,
+ 0 /* z */,
+ 0 /* currFrame */,
+ properties
+ );
+ }
+
+ private flags = 0;
+}
+
+export {LayerBuilder};
diff --git a/tools/winscope/src/test/unit/mock_storage.ts b/tools/winscope/src/test/unit/mock_storage.ts
new file mode 100644
index 0000000..7c66983
--- /dev/null
+++ b/tools/winscope/src/test/unit/mock_storage.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 class MockStorage implements Storage {
+ private store: {[key: string]: string} = {};
+
+ [name: string]: any;
+ get length(): number {
+ return Object.keys(this.store).length;
+ }
+
+ clear(): void {
+ throw new Error('Method not implemented.');
+ }
+ getItem(key: string): string | null {
+ return this.store[key];
+ }
+ key(index: number): string | null {
+ return Object.keys(this.store)[index];
+ }
+ removeItem(key: string): void {
+ delete this.store[key];
+ }
+ setItem(key: string, value: string): void {
+ this.store[key] = value;
+ }
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/test/unit/mock_storage_test.ts
similarity index 63%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/test/unit/mock_storage_test.ts
index bfe19ce..7ebaf2d 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/test/unit/mock_storage_test.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {MockStorage} from './mock_storage';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+describe('MockStorage', () => {
+ it('can store values', () => {
+ const mockStorage = new MockStorage();
+
+ mockStorage.setItem('key', 'value');
+
+ expect(mockStorage.getItem('key')).toBe('value');
+ });
+});
diff --git a/tools/winscope/src/test/unit/trace_builder.ts b/tools/winscope/src/test/unit/trace_builder.ts
new file mode 100644
index 0000000..e4edb55
--- /dev/null
+++ b/tools/winscope/src/test/unit/trace_builder.ts
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2022 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 {FrameMap} from 'trace/frame_map';
+import {FrameMapBuilder} from 'trace/frame_map_builder';
+import {AbsoluteEntryIndex, AbsoluteFrameIndex, EntriesRange} from 'trace/index_types';
+import {Parser} from 'trace/parser';
+import {ParserMock} from 'trace/parser_mock';
+import {Timestamp, TimestampType} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {TraceType} from 'trace/trace_type';
+
+export class TraceBuilder<T> {
+ private type = TraceType.SURFACE_FLINGER;
+ private parser?: Parser<T>;
+ private entries?: T[];
+ private timestamps?: Timestamp[];
+ private timestampType = TimestampType.REAL;
+ private frameMap?: FrameMap;
+ private frameMapBuilder?: FrameMapBuilder;
+ private descriptors: string[] = [];
+
+ setType(type: TraceType): TraceBuilder<T> {
+ this.type = type;
+ return this;
+ }
+
+ setParser(parser: Parser<T>): TraceBuilder<T> {
+ this.parser = parser;
+ return this;
+ }
+
+ setEntries(entries: T[]): TraceBuilder<T> {
+ this.entries = entries;
+ return this;
+ }
+
+ setTimestamps(timestamps: Timestamp[]): TraceBuilder<T> {
+ this.timestamps = timestamps;
+ return this;
+ }
+
+ setTimestampType(type: TimestampType): TraceBuilder<T> {
+ this.timestampType = type;
+ return this;
+ }
+
+ setFrameMap(frameMap?: FrameMap): TraceBuilder<T> {
+ this.frameMap = frameMap;
+ return this;
+ }
+
+ setFrame(entry: AbsoluteEntryIndex, frame: AbsoluteFrameIndex) {
+ if (!this.entries) {
+ throw new Error(`Can't set frames before specifying the entries`);
+ }
+ if (!this.frameMapBuilder) {
+ this.frameMapBuilder = new FrameMapBuilder(this.entries.length, 1000);
+ }
+ this.frameMapBuilder.setFrames(entry, {start: frame, end: frame + 1});
+ return this;
+ }
+
+ setDescriptors(descriptors: string[]): TraceBuilder<T> {
+ this.descriptors = descriptors;
+ return this;
+ }
+
+ build(): Trace<T> {
+ if (!this.parser) {
+ this.parser = this.createParser();
+ }
+
+ const entriesRange: EntriesRange = {
+ start: 0,
+ end: this.parser.getLengthEntries(),
+ };
+ const trace = Trace.newInitializedTrace<T>(
+ this.type,
+ this.parser,
+ this.descriptors,
+ this.timestampType,
+ entriesRange
+ );
+
+ const frameMap = this.getFrameMap();
+ if (frameMap) {
+ trace.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ return trace;
+ }
+
+ private createParser(): Parser<T> {
+ if (!this.timestamps && !this.entries) {
+ throw new Error(`Either the timestamps or the entries should be specified`);
+ }
+
+ if (!this.timestamps) {
+ this.timestamps = this.createTimestamps(this.entries as T[]);
+ }
+
+ if (!this.entries) {
+ this.entries = this.createEntries(this.timestamps);
+ }
+
+ if (this.entries.length !== this.timestamps.length) {
+ throw new Error('Entries and timestamps arrays must have the same length');
+ }
+
+ return new ParserMock(this.timestamps, this.entries);
+ }
+
+ private createTimestamps(entries: T[]): Timestamp[] {
+ const timestamps = new Array<Timestamp>();
+ for (let i = 0; i < entries.length; ++i) {
+ timestamps[i] = new Timestamp(TimestampType.REAL, BigInt(i));
+ }
+ return timestamps;
+ }
+
+ private createEntries(timestamps: Timestamp[]): T[] {
+ const entries = new Array<T>();
+ for (let i = 0; i < timestamps.length; ++i) {
+ entries.push(`entry-${i}` as unknown as T);
+ }
+ return entries;
+ }
+
+ private getFrameMap(): FrameMap | undefined {
+ if (this.frameMap && this.frameMapBuilder) {
+ throw new Error(
+ `Cannot set a full frame map as well as individual entry's frames. Pick one of the two options.`
+ );
+ }
+ if (this.frameMap) {
+ return this.frameMap;
+ }
+ if (this.frameMapBuilder) {
+ return this.frameMapBuilder.build();
+ }
+ return undefined;
+ }
+}
diff --git a/tools/winscope/src/test/unit/trace_utils.ts b/tools/winscope/src/test/unit/trace_utils.ts
new file mode 100644
index 0000000..e349a7c
--- /dev/null
+++ b/tools/winscope/src/test/unit/trace_utils.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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 {Timestamp} from 'trace/timestamp';
+import {AbsoluteFrameIndex, Trace} from 'trace/trace';
+
+export class TraceUtils {
+ static extractEntries<T>(trace: Trace<T>): T[] {
+ const entries = new Array<T>();
+ trace.forEachEntry((entry) => {
+ entries.push(entry.getValue());
+ });
+ return entries;
+ }
+
+ static extractTimestamps<T>(trace: Trace<T>): Timestamp[] {
+ const timestamps = new Array<Timestamp>();
+ trace.forEachTimestamp((timestamp) => {
+ timestamps.push(timestamp);
+ });
+ return timestamps;
+ }
+
+ static extractFrames<T>(trace: Trace<T>): Map<AbsoluteFrameIndex, T[]> {
+ const frames = new Map<AbsoluteFrameIndex, T[]>();
+ trace.forEachFrame((frame, index) => {
+ frames.set(index, TraceUtils.extractEntries(frame));
+ });
+ return frames;
+ }
+}
diff --git a/tools/winscope/src/test/unit/traces_builder.ts b/tools/winscope/src/test/unit/traces_builder.ts
new file mode 100644
index 0000000..a7c8a97
--- /dev/null
+++ b/tools/winscope/src/test/unit/traces_builder.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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 {FrameMap} from 'trace/frame_map';
+import {Timestamp} from 'trace/timestamp';
+import {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {TraceBuilder} from './trace_builder';
+
+export class TracesBuilder {
+ private readonly traceBuilders = new Map<TraceType, TraceBuilder<{}>>();
+
+ setEntries(type: TraceType, entries: Array<{}>): TracesBuilder {
+ const builder = this.getOrCreateTraceBuilder(type);
+ builder.setEntries(entries);
+ return this;
+ }
+
+ setTimestamps(type: TraceType, timestamps: Timestamp[]): TracesBuilder {
+ const builder = this.getOrCreateTraceBuilder(type);
+ builder.setTimestamps(timestamps);
+ return this;
+ }
+
+ setFrameMap(type: TraceType, frameMap: FrameMap | undefined): TracesBuilder {
+ const builder = this.getOrCreateTraceBuilder(type);
+ builder.setFrameMap(frameMap);
+ return this;
+ }
+
+ build(): Traces {
+ const traces = new Traces();
+ this.traceBuilders.forEach((builder, type) => {
+ traces.setTrace(type, builder.build());
+ });
+ return traces;
+ }
+
+ private getOrCreateTraceBuilder(type: TraceType): TraceBuilder<{}> {
+ let builder = this.traceBuilders.get(type);
+ if (!builder) {
+ builder = new TraceBuilder<{}>();
+ builder.setType(type);
+ this.traceBuilders.set(type, builder);
+ }
+ return builder;
+ }
+}
diff --git a/tools/winscope/src/test/unit/traces_utils.ts b/tools/winscope/src/test/unit/traces_utils.ts
new file mode 100644
index 0000000..40a1fcf
--- /dev/null
+++ b/tools/winscope/src/test/unit/traces_utils.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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 {AbsoluteFrameIndex} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {TraceUtils} from './trace_utils';
+
+export class TracesUtils {
+ static extractEntries(traces: Traces): Map<TraceType, Array<{}>> {
+ const entries = new Map<TraceType, Array<{}>>();
+
+ traces.forEachTrace((trace) => {
+ entries.set(trace.type, TraceUtils.extractEntries(trace));
+ });
+
+ return entries;
+ }
+
+ static extractFrames(traces: Traces): Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>> {
+ const frames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+
+ traces.forEachFrame((frame, index) => {
+ frames.set(index, new Map<TraceType, Array<{}>>());
+ frame.forEachTrace((trace, type) => {
+ frames.get(index)?.set(type, TraceUtils.extractEntries(trace));
+ });
+ });
+
+ return frames;
+ }
+}
diff --git a/tools/winscope/src/test/unit/utils.ts b/tools/winscope/src/test/unit/utils.ts
new file mode 100644
index 0000000..6575317
--- /dev/null
+++ b/tools/winscope/src/test/unit/utils.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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 {ParserFactory} from 'parsers/parser_factory';
+import {CommonTestUtils} from 'test/common/utils';
+import {LayerTraceEntry, WindowManagerState} from 'trace/flickerlib/common';
+import {Parser} from 'trace/parser';
+import {TimestampType} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {TraceFile} from 'trace/trace_file';
+import {TraceType} from 'trace/trace_type';
+
+class UnitTestUtils extends CommonTestUtils {
+ static async getTraceFromFile(filename: string): Promise<Trace<object>> {
+ const parser = await UnitTestUtils.getParser(filename);
+
+ const trace = Trace.newUninitializedTrace(parser);
+ trace.init(
+ parser.getTimestamps(TimestampType.REAL) !== undefined
+ ? TimestampType.REAL
+ : TimestampType.ELAPSED
+ );
+ return trace;
+ }
+
+ static async getParser(filename: string): Promise<Parser<object>> {
+ const file = new TraceFile(await CommonTestUtils.getFixtureFile(filename), undefined);
+ const [parsers, errors] = await new ParserFactory().createParsers([file]);
+ expect(parsers.length).toEqual(1);
+ return parsers[0].parser;
+ }
+
+ static async getWindowManagerState(): Promise<WindowManagerState> {
+ return UnitTestUtils.getTraceEntry('traces/elapsed_timestamp/WindowManager.pb');
+ }
+
+ static async getLayerTraceEntry(): Promise<LayerTraceEntry> {
+ return await UnitTestUtils.getTraceEntry('traces/elapsed_timestamp/SurfaceFlinger.pb');
+ }
+
+ static async getMultiDisplayLayerTraceEntry(): Promise<LayerTraceEntry> {
+ return await UnitTestUtils.getTraceEntry(
+ 'traces/elapsed_and_real_timestamp/SurfaceFlinger_multidisplay.pb'
+ );
+ }
+
+ static async getImeTraceEntries(): Promise<Map<TraceType, any>> {
+ let surfaceFlingerEntry: LayerTraceEntry | undefined;
+ {
+ const parser = await UnitTestUtils.getParser('traces/ime/SurfaceFlinger_with_IME.pb');
+ surfaceFlingerEntry = await parser.getEntry(5, TimestampType.ELAPSED);
+ }
+
+ let windowManagerEntry: WindowManagerState | undefined;
+ {
+ const parser = await UnitTestUtils.getParser('traces/ime/WindowManager_with_IME.pb');
+ windowManagerEntry = await parser.getEntry(2, TimestampType.ELAPSED);
+ }
+
+ const entries = new Map<TraceType, any>();
+ entries.set(
+ TraceType.INPUT_METHOD_CLIENTS,
+ await UnitTestUtils.getTraceEntry('traces/ime/InputMethodClients.pb')
+ );
+ entries.set(
+ TraceType.INPUT_METHOD_MANAGER_SERVICE,
+ await UnitTestUtils.getTraceEntry('traces/ime/InputMethodManagerService.pb')
+ );
+ entries.set(
+ TraceType.INPUT_METHOD_SERVICE,
+ await UnitTestUtils.getTraceEntry('traces/ime/InputMethodService.pb')
+ );
+ entries.set(TraceType.SURFACE_FLINGER, surfaceFlingerEntry);
+ entries.set(TraceType.WINDOW_MANAGER, windowManagerEntry);
+
+ return entries;
+ }
+
+ private static async getTraceEntry(filename: string) {
+ const parser = await UnitTestUtils.getParser(filename);
+ return parser.getEntry(0, TimestampType.ELAPSED);
+ }
+}
+
+export {UnitTestUtils};
diff --git a/tools/winscope/src/config/Configuration.json b/tools/winscope/src/trace/flickerlib/Configuration.json
similarity index 85%
rename from tools/winscope/src/config/Configuration.json
rename to tools/winscope/src/trace/flickerlib/Configuration.json
index b0f7ae9..857ba30 100644
--- a/tools/winscope/src/config/Configuration.json
+++ b/tools/winscope/src/trace/flickerlib/Configuration.json
@@ -14,6 +14,13 @@
"visibilityReason",
"absoluteZ",
"children",
+ "childWindows",
+ "childContainers",
+ "windowToken",
+ "rootDisplayArea",
+ "rootWindowContainer",
+ "windowContainer",
+ "children",
"stableId"
],
"intDefColumn": {
@@ -26,6 +33,7 @@
"WindowLayoutParams.subtreeSystemUiVisibilityFlags": "android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags",
"WindowLayoutParams.behavior": "android.view.WindowInsetsController.Behavior",
"WindowLayoutParams.fitInsetsSides": "android.view.WindowInsets.Side.InsetsSide",
+ "InputWindowInfoProto.layoutParamsFlags": "android.view.WindowManager.LayoutParams.Flags",
"Configuration.windowingMode": "android.app.WindowConfiguration.WindowingMode",
"WindowConfiguration.windowingMode": "android.app.WindowConfiguration.WindowingMode",
"Configuration.orientation": "android.content.pm.ActivityInfo.ScreenOrientation",
diff --git a/tools/winscope/src/trace/flickerlib/ObjectFormatter.ts b/tools/winscope/src/trace/flickerlib/ObjectFormatter.ts
new file mode 100644
index 0000000..c376771
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/ObjectFormatter.ts
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2021, 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 {ArrayUtils} from 'common/array_utils';
+import {PropertiesDump} from 'viewers/common/ui_tree_utils';
+import intDefMapping from '../../../../../../prebuilts/misc/common/winscope/intDefMapping.json';
+import {
+ toActiveBuffer,
+ toColor,
+ toColor3,
+ toInsets,
+ toMatrix22,
+ toPoint,
+ toPointF,
+ toRect,
+ toRectF,
+ toRegion,
+ toSize,
+ toTransform,
+} from './common';
+import config from './Configuration.json';
+
+function readIntdefMap(): Map<string, string> {
+ const map = new Map<string, string>();
+ const keys = Object.keys(config.intDefColumn);
+
+ keys.forEach((key) => {
+ const value = config.intDefColumn[key as keyof typeof config.intDefColumn];
+ map.set(key, value);
+ });
+
+ return map;
+}
+
+export class ObjectFormatter {
+ static displayDefaults: boolean = false;
+ private static INVALID_ELEMENT_PROPERTIES = config.invalidProperties;
+
+ private static FLICKER_INTDEF_MAP = readIntdefMap();
+
+ static cloneObject(entry: any): any {
+ const obj: any = {};
+ const properties = ObjectFormatter.getProperties(entry);
+ properties.forEach((prop) => (obj[prop] = entry[prop]));
+ return obj;
+ }
+
+ /**
+ * Get the true properties of an entry excluding functions, kotlin gernerated
+ * variables, explicitly excluded properties, and flicker objects already in
+ * the hierarchy that shouldn't be traversed when formatting the entry
+ * @param entry The entry for which we want to get the properties for
+ * @return The "true" properties of the entry as described above
+ */
+ static getProperties(entry: any): string[] {
+ const props: string[] = [];
+ let obj = entry;
+
+ do {
+ const properties = Object.getOwnPropertyNames(obj).filter((it) => {
+ // filter out functions
+ if (typeof entry[it] === 'function') return false;
+ // internal propertires from kotlinJs
+ if (it.includes(`$`)) return false;
+ // private kotlin variables from kotlin
+ if (it.startsWith(`_`)) return false;
+ // some predefined properties used only internally (e.g., children, ref, diff)
+ if (ObjectFormatter.INVALID_ELEMENT_PROPERTIES.includes(it)) return false;
+
+ const value = entry[it];
+ // only non-empty arrays of non-flicker objects (otherwise they are in hierarchy)
+ if (Array.isArray(value) && value.length > 0) return !value[0].stableId;
+ // non-flicker object
+ return !value?.stableId;
+ });
+ properties.forEach((prop) => {
+ if (typeof entry[prop] !== 'function' && props.indexOf(prop) === -1) {
+ props.push(prop);
+ }
+ });
+ obj = Object.getPrototypeOf(obj);
+ } while (obj);
+
+ return props;
+ }
+
+ /**
+ * Format a Winscope entry to be displayed in the UI
+ * Accounts for different user display settings (e.g. hiding empty/default values)
+ * @param obj The raw object to format
+ * @return The formatted object
+ */
+ static format(obj: any): PropertiesDump {
+ const properties = ObjectFormatter.getProperties(obj);
+ const sortedProperties = properties.sort();
+
+ const result: PropertiesDump = {};
+ sortedProperties.forEach((entry) => {
+ const key = entry;
+ const value: any = obj[key];
+
+ if (value === null || value === undefined) {
+ if (ObjectFormatter.displayDefaults) {
+ result[key] = value;
+ }
+ return;
+ }
+
+ if (value || ObjectFormatter.displayDefaults) {
+ // raw values (e.g., false or 0)
+ if (!value) {
+ result[key] = value;
+ // flicker obj
+ } else if (value.prettyPrint) {
+ const isEmpty = value.isEmpty === true;
+ if (!isEmpty || ObjectFormatter.displayDefaults) {
+ result[key] = value.prettyPrint();
+ }
+ } else {
+ // converted proto to flicker
+ const translatedObject = ObjectFormatter.translateObject(key, value);
+ if (translatedObject) {
+ if (translatedObject.prettyPrint) {
+ result[key] = translatedObject.prettyPrint();
+ } else {
+ result[key] = translatedObject;
+ }
+ // objects - recursive call
+ } else if (value && typeof value === `object`) {
+ const childObj = ObjectFormatter.format(value) as any;
+ const isEmpty = Object.entries(childObj).length === 0 || childObj.isEmpty;
+ if (!isEmpty || ObjectFormatter.displayDefaults) {
+ result[key] = childObj;
+ }
+ } else {
+ // values
+ result[key] = ObjectFormatter.translateIntDef(obj, key, value);
+ }
+ }
+ }
+ });
+
+ return result;
+ }
+
+ /**
+ * Translate some predetermined proto objects into their flicker equivalent
+ *
+ * Returns null if the object cannot be translated
+ *
+ * @param obj Object to translate
+ */
+ private static translateObject(key: string, obj: any) {
+ const type = obj?.$type?.name ?? obj?.constructor?.name;
+ switch (type) {
+ case `SizeProto`:
+ return toSize(obj);
+ case `ActiveBufferProto`:
+ return toActiveBuffer(obj);
+ case `Color3`:
+ return toColor3(obj);
+ case `ColorProto`:
+ return toColor(obj);
+ case `Long`:
+ return obj?.toString();
+ case `PointProto`:
+ return toPoint(obj);
+ case `PositionProto`:
+ return toPointF(obj);
+ // It is necessary to check for a keyword insets because the proto
+ // definition of insets and rects uses the same object type
+ case `RectProto`:
+ return key.toLowerCase().includes('insets') ? toInsets(obj) : toRect(obj);
+ case `Matrix22`:
+ return toMatrix22(obj);
+ case `FloatRectProto`:
+ return toRectF(obj);
+ case `RegionProto`:
+ return toRegion(obj);
+ case `TransformProto`:
+ return toTransform(obj);
+ case 'ColorTransformProto': {
+ const formatted = ObjectFormatter.formatColorTransform(obj.val);
+ return `${formatted}`;
+ }
+ default:
+ // handle other cases below
+ }
+
+ // Raw long number (no type name, no constructor name, no useful toString() method)
+ if (ArrayUtils.equal(Object.keys(obj).sort(), ['high_', 'low_'])) {
+ const high = BigInt(obj.high_) << 32n;
+ let low = BigInt(obj.low_);
+ if (low < 0) {
+ low = -low;
+ }
+ return (high | low).toString();
+ }
+
+ return null;
+ }
+
+ private static formatColorTransform(vals: any) {
+ const fixedVals = vals.map((v: any) => 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;
+ }
+
+ /**
+ * Obtains from the proto field, the metadata related to the typedef type (if any)
+ *
+ * @param obj Proto object
+ * @param propertyName Property to search
+ */
+ private static getTypeDefSpec(obj: any, propertyName: string): string | null {
+ const fields = obj?.$type?.fields;
+ if (!fields) {
+ return null;
+ }
+
+ const options = fields[propertyName]?.options;
+ if (!options) {
+ return null;
+ }
+
+ return options['(.android.typedef)'];
+ }
+
+ /**
+ * Translate intdef properties into their string representation
+ *
+ * For proto objects check the
+ *
+ * @param parentObj Object containing the value to parse
+ * @param propertyName Property to search
+ * @param value Property value
+ */
+ private static translateIntDef(parentObj: any, propertyName: string, value: any): string {
+ const parentClassName = parentObj.constructor.name;
+ const propertyPath = `${parentClassName}.${propertyName}`;
+
+ let translatedValue: string = value;
+ // Parse Flicker objects (no intdef annotation supported)
+ if (ObjectFormatter.FLICKER_INTDEF_MAP.has(propertyPath)) {
+ translatedValue = ObjectFormatter.getIntFlagsAsStrings(
+ value,
+ ObjectFormatter.FLICKER_INTDEF_MAP.get(propertyPath) as string
+ );
+ } else {
+ // If it's a proto, search on the proto definition for the intdef type
+ const typeDefSpec = ObjectFormatter.getTypeDefSpec(parentObj, propertyName);
+ if (typeDefSpec) {
+ translatedValue = ObjectFormatter.getIntFlagsAsStrings(value, typeDefSpec);
+ }
+ }
+
+ return translatedValue;
+ }
+
+ /**
+ * Translate a property from its numerical value into its string representation
+ *
+ * @param intFlags Property value
+ * @param annotationType IntDef type to use
+ */
+ private static getIntFlagsAsStrings(intFlags: any, annotationType: string): string {
+ const flags = [];
+
+ const mapping = intDefMapping[annotationType as keyof typeof intDefMapping].values;
+ const knownFlagValues = Object.keys(mapping)
+ .reverse()
+ .map((x) => Math.floor(Number(x)));
+
+ if (knownFlagValues.length === 0) {
+ console.warn('No mapping for type', annotationType);
+ return intFlags + '';
+ }
+
+ // Will only contain bits that have not been associated with a flag.
+ const parsedIntFlags = Math.floor(Number(intFlags));
+ let leftOver = parsedIntFlags;
+
+ for (const flagValue of knownFlagValues) {
+ if (
+ (leftOver & flagValue && (intFlags & flagValue) === flagValue) ||
+ (parsedIntFlags === 0 && flagValue === 0)
+ ) {
+ flags.push(mapping[flagValue as keyof typeof mapping]);
+
+ leftOver = leftOver & ~flagValue;
+ }
+ }
+
+ 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(' | ');
+ }
+}
diff --git a/tools/winscope/src/flickerlib/README.md b/tools/winscope/src/trace/flickerlib/README.md
similarity index 100%
rename from tools/winscope/src/flickerlib/README.md
rename to tools/winscope/src/trace/flickerlib/README.md
diff --git a/tools/winscope/src/trace/flickerlib/common.js b/tools/winscope/src/trace/flickerlib/common.js
new file mode 100644
index 0000000..278e26b
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/common.js
@@ -0,0 +1,366 @@
+/*
+ * 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
+
+// WM
+const WindowManagerTrace = require('flicker').android.tools.common.traces.wm.WindowManagerTrace;
+const WindowManagerState = require('flicker').android.tools.common.traces.wm.WindowManagerState;
+const WindowManagerTraceEntryBuilder =
+ require('flicker').android.tools.common.traces.wm.WindowManagerTraceEntryBuilder;
+const Activity = require('flicker').android.tools.common.traces.wm.Activity;
+const Configuration = require('flicker').android.tools.common.traces.wm.Configuration;
+const ConfigurationContainer =
+ require('flicker').android.tools.common.traces.wm.ConfigurationContainer;
+const DisplayArea = require('flicker').android.tools.common.traces.wm.DisplayArea;
+const DisplayContent = require('flicker').android.tools.common.traces.wm.DisplayContent;
+const DisplayCutout = require('flicker').android.tools.common.traces.wm.DisplayCutout;
+const KeyguardControllerState =
+ require('flicker').android.tools.common.traces.wm.KeyguardControllerState;
+const RootWindowContainer = require('flicker').android.tools.common.traces.wm.RootWindowContainer;
+const Task = require('flicker').android.tools.common.traces.wm.Task;
+const TaskFragment = require('flicker').android.tools.common.traces.wm.TaskFragment;
+const WindowConfiguration = require('flicker').android.tools.common.traces.wm.WindowConfiguration;
+const WindowContainer = require('flicker').android.tools.common.traces.wm.WindowContainer;
+const WindowLayoutParams = require('flicker').android.tools.common.traces.wm.WindowLayoutParams;
+const WindowManagerPolicy = require('flicker').android.tools.common.traces.wm.WindowManagerPolicy;
+const WindowState = require('flicker').android.tools.common.traces.wm.WindowState;
+const WindowToken = require('flicker').android.tools.common.traces.wm.WindowToken;
+
+// SF
+const Layer = require('flicker').android.tools.common.traces.surfaceflinger.Layer;
+const LayerProperties =
+ require('flicker').android.tools.common.traces.surfaceflinger.LayerProperties;
+const LayerTraceEntry =
+ require('flicker').android.tools.common.traces.surfaceflinger.LayerTraceEntry;
+const LayerTraceEntryBuilder =
+ require('flicker').android.tools.common.traces.surfaceflinger.LayerTraceEntryBuilder;
+const LayersTrace = require('flicker').android.tools.common.traces.surfaceflinger.LayersTrace;
+const Transform = require('flicker').android.tools.common.traces.surfaceflinger.Transform;
+const Display = require('flicker').android.tools.common.traces.surfaceflinger.Display;
+const Region = require('flicker').android.tools.common.datatypes.Region;
+
+// Event Log
+const EventLog = require('flicker').android.tools.common.traces.events.EventLog;
+const CujEvent = require('flicker').android.tools.common.traces.events.CujEvent;
+const CujType = require('flicker').android.tools.common.traces.events.CujType;
+const Event = require('flicker').android.tools.common.traces.events.Event;
+const FlickerEvent = require('flicker').android.tools.common.traces.events.FlickerEvent;
+const FocusEvent = require('flicker').android.tools.common.traces.events.FocusEvent;
+const EventLogParser = require('flicker').android.tools.common.parsers.events.EventLogParser;
+const CujTrace = require('flicker').android.tools.common.parsers.events.CujTrace;
+const Cuj = require('flicker').android.tools.common.parsers.events.Cuj;
+
+// Transitions
+const Transition = require('flicker').android.tools.common.traces.wm.Transition;
+const TransitionType = require('flicker').android.tools.common.traces.wm.TransitionType;
+const TransitionChange = require('flicker').android.tools.common.traces.wm.TransitionChange;
+const TransitionsTrace = require('flicker').android.tools.common.traces.wm.TransitionsTrace;
+const ShellTransitionData = require('flicker').android.tools.common.traces.wm.ShellTransitionData;
+const WmTransitionData = require('flicker').android.tools.common.traces.wm.WmTransitionData;
+
+// Common
+const Size = require('flicker').android.tools.common.datatypes.Size;
+const ActiveBuffer = require('flicker').android.tools.common.datatypes.ActiveBuffer;
+const Color3 = require('flicker').android.tools.common.datatypes.Color3;
+const Color = require('flicker').android.tools.common.datatypes.Color;
+const Insets = require('flicker').android.tools.common.datatypes.Insets;
+const Matrix22 = require('flicker').android.tools.common.datatypes.Matrix22;
+const Matrix33 = require('flicker').android.tools.common.datatypes.Matrix33;
+const PlatformConsts = require('flicker').android.tools.common.PlatformConsts;
+const Rotation = require('flicker').android.tools.common.Rotation;
+const Point = require('flicker').android.tools.common.datatypes.Point;
+const PointF = require('flicker').android.tools.common.datatypes.PointF;
+const Rect = require('flicker').android.tools.common.datatypes.Rect;
+const RectF = require('flicker').android.tools.common.datatypes.RectF;
+const WindowingMode = require('flicker').android.tools.common.traces.wm.WindowingMode;
+const CrossPlatform = require('flicker').android.tools.common.CrossPlatform;
+const TimestampFactory = require('flicker').android.tools.common.TimestampFactory;
+
+const EMPTY_SIZE = Size.Companion.EMPTY;
+const EMPTY_BUFFER = ActiveBuffer.Companion.EMPTY;
+const EMPTY_COLOR3 = Color3.Companion.EMPTY;
+const EMPTY_COLOR = Color.Companion.EMPTY;
+const EMPTY_INSETS = Insets.Companion.EMPTY;
+const EMPTY_RECT = Rect.Companion.EMPTY;
+const EMPTY_RECTF = RectF.Companion.EMPTY;
+const EMPTY_POINT = Point.Companion.EMPTY;
+const EMPTY_POINTF = PointF.Companion.EMPTY;
+const EMPTY_MATRIX22 = Matrix22.Companion.EMPTY;
+const EMPTY_MATRIX33 = Matrix33.Companion.identity(0, 0);
+const EMPTY_TRANSFORM = new Transform(0, EMPTY_MATRIX33);
+
+function toSize(proto) {
+ if (proto == null) {
+ return EMPTY_SIZE;
+ }
+ const width = proto.width ?? proto.w ?? 0;
+ const height = proto.height ?? proto.h ?? 0;
+ if (width || height) {
+ return new Size(width, height);
+ }
+ return EMPTY_SIZE;
+}
+
+function toActiveBuffer(proto) {
+ const width = proto?.width ?? 0;
+ const height = proto?.height ?? 0;
+ const stride = proto?.stride ?? 0;
+ const format = proto?.format ?? 0;
+
+ if (width || height || stride || format) {
+ return new ActiveBuffer(width, height, stride, format);
+ }
+ return EMPTY_BUFFER;
+}
+
+function toColor3(proto) {
+ if (proto == null) {
+ return EMPTY_COLOR;
+ }
+ const r = proto.r ?? 0;
+ const g = proto.g ?? 0;
+ const b = proto.b ?? 0;
+ if (r || g || b) {
+ return new Color3(r, g, b);
+ }
+ return EMPTY_COLOR3;
+}
+
+function toColor(proto) {
+ if (proto == null) {
+ return EMPTY_COLOR;
+ }
+ const r = proto.r ?? 0;
+ const g = proto.g ?? 0;
+ const b = proto.b ?? 0;
+ const a = proto.a ?? 0;
+ if (r || g || b || a) {
+ return new Color(r, g, b, a);
+ }
+ return EMPTY_COLOR;
+}
+
+function toPoint(proto) {
+ if (proto == null) {
+ return null;
+ }
+ const x = proto.x ?? 0;
+ const y = proto.y ?? 0;
+ if (x || y) {
+ return new Point(x, y);
+ }
+ return EMPTY_POINT;
+}
+
+function toPointF(proto) {
+ if (proto == null) {
+ return null;
+ }
+ const x = proto.x ?? 0;
+ const y = proto.y ?? 0;
+ if (x || y) {
+ return new PointF(x, y);
+ }
+ return EMPTY_POINTF;
+}
+
+function toInsets(proto) {
+ if (proto == null) {
+ return EMPTY_INSETS;
+ }
+
+ const left = proto?.left ?? 0;
+ const top = proto?.top ?? 0;
+ const right = proto?.right ?? 0;
+ const bottom = proto?.bottom ?? 0;
+ if (left || top || right || bottom) {
+ return new Insets(left, top, right, bottom);
+ }
+ return EMPTY_INSETS;
+}
+
+function toRect(proto) {
+ if (proto == null) {
+ return EMPTY_RECT;
+ }
+
+ const left = proto?.left ?? 0;
+ const top = proto?.top ?? 0;
+ const right = proto?.right ?? 0;
+ const bottom = proto?.bottom ?? 0;
+ if (left || top || right || bottom) {
+ return new Rect(left, top, right, bottom);
+ }
+ return EMPTY_RECT;
+}
+
+function toRectF(proto) {
+ if (proto == null) {
+ return EMPTY_RECTF;
+ }
+
+ const left = proto?.left ?? 0;
+ const top = proto?.top ?? 0;
+ const right = proto?.right ?? 0;
+ const bottom = proto?.bottom ?? 0;
+ if (left || top || right || bottom) {
+ return new RectF(left, top, right, bottom);
+ }
+ return EMPTY_RECTF;
+}
+
+function toRegion(proto) {
+ if (proto == null) {
+ return null;
+ }
+
+ const rects = [];
+ for (let x = 0; x < proto.rect.length; x++) {
+ const rect = proto.rect[x];
+ const parsedRect = toRect(rect);
+ rects.push(parsedRect);
+ }
+
+ return new Region(rects);
+}
+
+function toTransform(proto) {
+ if (proto == null) {
+ return EMPTY_TRANSFORM;
+ }
+ const dsdx = proto.dsdx ?? 0;
+ const dtdx = proto.dtdx ?? 0;
+ const tx = proto.tx ?? 0;
+ const dsdy = proto.dsdy ?? 0;
+ const dtdy = proto.dtdy ?? 0;
+ const ty = proto.ty ?? 0;
+
+ if (dsdx || dtdx || tx || dsdy || dtdy || ty) {
+ const matrix = new Matrix33(dsdx, dtdx, tx, dsdy, dtdy, ty);
+ return new Transform(proto.type ?? 0, matrix);
+ }
+
+ if (proto.type) {
+ return new Transform(proto.type ?? 0, EMPTY_MATRIX33);
+ }
+ return EMPTY_TRANSFORM;
+}
+
+function toMatrix22(proto) {
+ if (proto == null) {
+ return EMPTY_MATRIX22;
+ }
+ const dsdx = proto.dsdx ?? 0;
+ const dtdx = proto.dtdx ?? 0;
+ const dsdy = proto.dsdy ?? 0;
+ const dtdy = proto.dtdy ?? 0;
+
+ if (dsdx || dtdx || dsdy || dtdy) {
+ return new Matrix22(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ return EMPTY_MATRIX22;
+}
+
+export {
+ Activity,
+ Configuration,
+ ConfigurationContainer,
+ DisplayArea,
+ DisplayContent,
+ KeyguardControllerState,
+ DisplayCutout,
+ RootWindowContainer,
+ Task,
+ TaskFragment,
+ WindowConfiguration,
+ WindowContainer,
+ WindowState,
+ WindowToken,
+ WindowLayoutParams,
+ WindowManagerPolicy,
+ WindowManagerTrace,
+ WindowManagerState,
+ WindowManagerTraceEntryBuilder,
+ // SF
+ Layer,
+ LayerProperties,
+ LayerTraceEntry,
+ LayerTraceEntryBuilder,
+ LayersTrace,
+ Transform,
+ Matrix22,
+ Matrix33,
+ Display,
+ // Eventlog
+ EventLog,
+ CujEvent,
+ CujType,
+ Event,
+ FlickerEvent,
+ FocusEvent,
+ EventLogParser,
+ CujTrace,
+ Cuj,
+ // Transitions
+ Transition,
+ TransitionType,
+ TransitionChange,
+ TransitionsTrace,
+ ShellTransitionData,
+ WmTransitionData,
+ // Common
+ Size,
+ ActiveBuffer,
+ Color,
+ Color3,
+ Insets,
+ PlatformConsts,
+ Point,
+ Rect,
+ RectF,
+ Region,
+ Rotation,
+ WindowingMode,
+ CrossPlatform,
+ TimestampFactory,
+ // Service
+ toSize,
+ toActiveBuffer,
+ toColor,
+ toColor3,
+ toInsets,
+ toPoint,
+ toPointF,
+ toRect,
+ toRectF,
+ toRegion,
+ toMatrix22,
+ toTransform,
+ // Constants
+ EMPTY_BUFFER,
+ EMPTY_COLOR3,
+ EMPTY_COLOR,
+ EMPTY_RECT,
+ EMPTY_RECTF,
+ EMPTY_POINT,
+ EMPTY_POINTF,
+ EMPTY_MATRIX22,
+ EMPTY_MATRIX33,
+ EMPTY_TRANSFORM,
+};
diff --git a/tools/winscope/src/trace/flickerlib/layers/Layer.ts b/tools/winscope/src/trace/flickerlib/layers/Layer.ts
new file mode 100644
index 0000000..1ad75e6
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/layers/Layer.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2021, 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 {
+ Layer,
+ LayerProperties,
+ Rect,
+ toActiveBuffer,
+ toColor,
+ toRect,
+ toRectF,
+ toRegion,
+} from '../common';
+import {shortenName} from '../mixin';
+import {Transform} from './Transform';
+
+Layer.fromProto = (proto: any, excludesCompositionState = false): Layer => {
+ const visibleRegion = toRegion(proto.visibleRegion);
+ const activeBuffer = toActiveBuffer(proto.activeBuffer);
+ const bounds = toRectF(proto.bounds);
+ const color = toColor(proto.color);
+ const screenBounds = toRectF(proto.screenBounds);
+ const sourceBounds = toRectF(proto.sourceBounds);
+ const transform = Transform.fromProto(proto.transform, proto.position);
+ const bufferTransform = Transform.fromProto(proto.bufferTransform, /* position */ null);
+ const hwcCrop = toRectF(proto.hwcCrop);
+ const hwcFrame = toRect(proto.hwcFrame);
+ const requestedColor = toColor(proto.requestedColor);
+ const requestedTransform = Transform.fromProto(proto.requestedTransform, proto.requestedPosition);
+ const cornerRadiusCrop = toRectF(proto.cornerRadiusCrop);
+ const inputTransform = Transform.fromProto(
+ proto.inputWindowInfo ? proto.inputWindowInfo.transform : null
+ );
+ const inputRegion = toRegion(
+ proto.inputWindowInfo ? proto.inputWindowInfo.touchableRegion : null
+ );
+ let crop: Rect;
+ if (proto.crop) {
+ crop = toRect(proto.crop);
+ }
+
+ const properties = new LayerProperties(
+ visibleRegion,
+ activeBuffer,
+ /* flags */ proto.flags,
+ bounds,
+ color,
+ /* isOpaque */ proto.isOpaque,
+ /* shadowRadius */ proto.shadowRadius,
+ /* cornerRadius */ proto.cornerRadius,
+ /* type */ proto.type ?? ``,
+ screenBounds,
+ transform,
+ sourceBounds,
+ /* effectiveScalingMode */ proto.effectiveScalingMode,
+ bufferTransform,
+ /* hwcCompositionType */ proto.hwcCompositionType,
+ hwcCrop,
+ hwcFrame,
+ /* backgroundBlurRadius */ proto.backgroundBlurRadius,
+ crop,
+ /* isRelativeOf */ proto.isRelativeOf,
+ /* zOrderRelativeOfId */ proto.zOrderRelativeOf,
+ /* stackId */ proto.layerStack,
+ requestedTransform,
+ requestedColor,
+ cornerRadiusCrop,
+ inputTransform,
+ inputRegion,
+ excludesCompositionState
+ );
+
+ const entry = new Layer(
+ /* name */ proto.name ?? ``,
+ /* id */ proto.id,
+ /*parentId */ proto.parent,
+ /* z */ proto.z,
+ /* currFrame */ proto.currFrame,
+ properties
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+};
+
+function addAttributes(entry: Layer, proto: any) {
+ entry.kind = `${entry.id}`;
+ entry.shortName = shortenName(entry.name);
+ entry.proto = proto;
+ entry.rect = entry.bounds;
+ entry.rect.transform = entry.transform;
+ entry.rect.ref = entry;
+ entry.rect.label = entry.name;
+}
+
+export {Layer};
diff --git a/tools/winscope/src/trace/flickerlib/layers/LayerTraceEntry.ts b/tools/winscope/src/trace/flickerlib/layers/LayerTraceEntry.ts
new file mode 100644
index 0000000..66c8fb0
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/layers/LayerTraceEntry.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2021, 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 {TimeUtils} from 'common/time_utils';
+import {ElapsedTimestamp, RealTimestamp} from 'trace/timestamp';
+import {
+ Display,
+ LayerTraceEntry,
+ LayerTraceEntryBuilder,
+ toRect,
+ toSize,
+ toTransform,
+} from '../common';
+import {getPropertiesForDisplay} from '../mixin';
+import {Layer} from './Layer';
+
+LayerTraceEntry.fromProto = (
+ protos: object[],
+ displayProtos: object[],
+ elapsedTimestamp: bigint,
+ vSyncId: number,
+ hwcBlob: string,
+ where = '',
+ realToElapsedTimeOffsetNs: bigint | undefined = undefined,
+ useElapsedTime = false,
+ excludesCompositionState = false
+): LayerTraceEntry => {
+ const layers = protos.map((it) => Layer.fromProto(it, excludesCompositionState));
+ const displays = (displayProtos || []).map((it) => newDisplay(it));
+ const builder = new LayerTraceEntryBuilder()
+ .setElapsedTimestamp(`${elapsedTimestamp}`)
+ .setLayers(layers)
+ .setDisplays(displays)
+ .setVSyncId(`${vSyncId}`)
+ .setHwcBlob(hwcBlob)
+ .setWhere(where)
+ .setRealToElapsedTimeOffsetNs(`${realToElapsedTimeOffsetNs ?? 0}`);
+ const entry: LayerTraceEntry = builder.build();
+
+ addAttributes(entry, protos, realToElapsedTimeOffsetNs === undefined || useElapsedTime);
+ return entry;
+};
+
+function addAttributes(entry: LayerTraceEntry, protos: object[], useElapsedTime = false) {
+ entry.kind = 'entry';
+ // Avoid parsing the entry root because it is an array of layers
+ // containing all trace information, this slows down the property tree.
+ // Instead parse only key properties for debugging
+ const newObj = getPropertiesForDisplay(entry);
+ if (newObj.rects) delete newObj.rects;
+ if (newObj.flattenedLayers) delete newObj.flattenedLayers;
+ if (newObj.physicalDisplays) delete newObj.physicalDisplays;
+ if (newObj.physicalDisplayBounds) delete newObj.physicalDisplayBounds;
+ if (newObj.isVisible) delete newObj.isVisible;
+ entry.proto = newObj;
+ if (useElapsedTime || entry.clockTimestamp === undefined) {
+ entry.name = TimeUtils.format(new ElapsedTimestamp(BigInt(entry.elapsedTimestamp)));
+ entry.shortName = entry.name;
+ } else {
+ entry.name = TimeUtils.format(new RealTimestamp(entry.clockTimestamp));
+ entry.shortName = entry.name;
+ }
+}
+
+function newDisplay(proto: any): Display {
+ return new Display(
+ `${proto.id}`,
+ proto.name,
+ proto.layerStack,
+ toSize(proto.size),
+ toRect(proto.layerStackSpaceRect),
+ toTransform(proto.transform),
+ proto.isVirtual
+ );
+}
+
+export {LayerTraceEntry};
diff --git a/tools/winscope/src/trace/flickerlib/layers/Transform.ts b/tools/winscope/src/trace/flickerlib/layers/Transform.ts
new file mode 100644
index 0000000..6015a2f
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/layers/Transform.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021, 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 {Matrix33, Transform} from '../common';
+
+Transform.fromProto = (transformProto: any, positionProto: any): Transform => {
+ const entry = new Transform(transformProto?.type ?? 0, getMatrix(transformProto, positionProto));
+
+ return entry;
+};
+
+function getMatrix(transform: any, position: any): Matrix33 {
+ const x = position?.x ?? 0;
+ const y = position?.y ?? 0;
+
+ if (transform == null || isSimpleTransform(transform.type)) {
+ return getDefaultTransform(transform?.type, x, y);
+ }
+
+ return new Matrix33(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y);
+}
+
+function getDefaultTransform(type: number, x: number, y: number): Matrix33 {
+ // IDENTITY
+ if (!type) {
+ return new Matrix33(1, 0, x, 0, 1, y);
+ }
+
+ // ROT_270 = ROT_90|FLIP_H|FLIP_V
+ if (isFlagSet(type, ROT_90_VAL | FLIP_V_VAL | FLIP_H_VAL)) {
+ return new Matrix33(0, -1, x, 1, 0, y);
+ }
+
+ // ROT_180 = FLIP_H|FLIP_V
+ if (isFlagSet(type, FLIP_V_VAL | FLIP_H_VAL)) {
+ return new Matrix33(-1, 0, x, 0, -1, y);
+ }
+
+ // ROT_90
+ if (isFlagSet(type, ROT_90_VAL)) {
+ return new Matrix33(0, 1, x, -1, 0, y);
+ }
+
+ // IDENTITY
+ if (isFlagClear(type, SCALE_VAL | ROTATE_VAL)) {
+ return new Matrix33(1, 0, x, 0, 1, y);
+ }
+
+ throw new Error(`Unknown transform type ${type}`);
+}
+
+export function isFlagSet(type: number, bits: number): boolean {
+ type = type || 0;
+ return (type & bits) === bits;
+}
+
+export function isFlagClear(type: number, bits: number): boolean {
+ return (type & bits) === 0;
+}
+
+export function isSimpleTransform(type: number): boolean {
+ return isFlagClear(type, ROT_INVALID_VAL | SCALE_VAL);
+}
+
+/* transform type flags */
+const ROTATE_VAL = 0x0002;
+const SCALE_VAL = 0x0004;
+
+/* orientation flags */
+const FLIP_H_VAL = 0x0100; // (1 << 0 << 8)
+const FLIP_V_VAL = 0x0200; // (1 << 1 << 8)
+const ROT_90_VAL = 0x0400; // (1 << 2 << 8)
+const ROT_INVALID_VAL = 0x8000; // (0x80 << 8)
+
+export {Transform};
diff --git a/tools/winscope/src/trace/flickerlib/mixin.ts b/tools/winscope/src/trace/flickerlib/mixin.ts
new file mode 100644
index 0000000..ca40f7a
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/mixin.ts
@@ -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 {ObjectFormatter} from './ObjectFormatter';
+
+/**
+ * Get the properties of a WM object for display.
+ *
+ * @param entry WM hierarchy element
+ * @param proto Associated proto object
+ */
+export function getPropertiesForDisplay(entry: any): any {
+ if (!entry) {
+ return;
+ }
+
+ let obj: any = {};
+ const properties = ObjectFormatter.getProperties(entry);
+ properties.forEach((prop) => (obj[prop] = entry[prop]));
+
+ // 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.
+ if (obj.children) delete obj.children;
+ if (obj.proto) delete obj.proto;
+
+ obj.proto = Object.assign({}, entry.proto);
+ if (obj.proto.children) delete obj.proto.children;
+ if (obj.proto.childWindows) delete obj.proto.childWindows;
+ if (obj.proto.childrenWindows) delete obj.proto.childrenWindows;
+ if (obj.proto.childContainers) delete obj.proto.childContainers;
+ if (obj.proto.windowToken) delete obj.proto.windowToken;
+ if (obj.proto.rootDisplayArea) delete obj.proto.rootDisplayArea;
+ if (obj.proto.rootWindowContainer) delete obj.proto.rootWindowContainer;
+ if (obj.proto.windowContainer?.children) delete obj.proto.windowContainer.children;
+ obj = ObjectFormatter.format(obj);
+
+ return obj;
+}
+
+export function shortenName(name: any): 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}`;
+}
diff --git a/tools/winscope/src/trace/flickerlib/windows/Activity.ts b/tools/winscope/src/trace/flickerlib/windows/Activity.ts
new file mode 100644
index 0000000..5ae9070
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/Activity.ts
@@ -0,0 +1,55 @@
+/*
+ * 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 {Activity} from '../common';
+import {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+Activity.fromProto = (proto: any, nextSeq: () => number): Activity => {
+ if (proto == null) {
+ return null;
+ } else {
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.windowToken.windowContainer,
+ /* protoChildren */ proto.windowToken.windowContainer?.children ?? [],
+ /* isActivityInTree */ true,
+ /* computedZ */ nextSeq,
+ /* nameOverride */ null,
+ /* identifierOverride */ proto.identifier
+ );
+
+ const entry = new Activity(
+ proto.name,
+ proto.state,
+ proto.visible,
+ proto.frontOfTask,
+ proto.procId,
+ proto.translucent,
+ windowContainer
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+ }
+};
+
+function addAttributes(entry: Activity, proto: any) {
+ entry.proto = proto;
+ entry.kind = entry.constructor.name;
+ entry.shortName = shortenName(entry.name);
+}
+
+export {Activity};
diff --git a/tools/winscope/src/trace/flickerlib/windows/DisplayArea.ts b/tools/winscope/src/trace/flickerlib/windows/DisplayArea.ts
new file mode 100644
index 0000000..7ed63ea
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/DisplayArea.ts
@@ -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 {DisplayArea} from '../common';
+import {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+DisplayArea.fromProto = (
+ proto: any,
+ isActivityInTree: boolean,
+ nextSeq: () => number
+): DisplayArea => {
+ if (proto == null) {
+ return null;
+ } else {
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.windowContainer,
+ /* protoChildren */ proto.windowContainer?.children ?? [],
+ /* isActivityInTree */ isActivityInTree,
+ /* computedZ */ nextSeq,
+ /* nameOverride */ proto.name
+ );
+
+ const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer);
+
+ addAttributes(entry, proto);
+ return entry;
+ }
+};
+
+function addAttributes(entry: DisplayArea, proto: any) {
+ entry.proto = proto;
+ entry.proto.configurationContainer = proto.windowContainer?.configurationContainer;
+ entry.proto.surfaceControl = proto.windowContainer?.surfaceControl;
+ entry.kind = entry.constructor.name;
+ entry.shortName = shortenName(entry.name);
+}
+
+export {DisplayArea};
diff --git a/tools/winscope/src/trace/flickerlib/windows/DisplayContent.ts b/tools/winscope/src/trace/flickerlib/windows/DisplayContent.ts
new file mode 100644
index 0000000..812b867
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/DisplayContent.ts
@@ -0,0 +1,91 @@
+/*
+ * 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, DisplayCutout, Rect, Rotation, toInsets, toRect} from '../common';
+import {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+DisplayContent.fromProto = (
+ proto: any,
+ isActivityInTree: boolean,
+ nextSeq: () => number
+): DisplayContent => {
+ if (proto == null) {
+ return null;
+ } else {
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.rootDisplayArea.windowContainer,
+ /* protoChildren */ proto.rootDisplayArea.windowContainer?.children ?? [],
+ /* isActivityInTree */ isActivityInTree,
+ /* computedZ */ nextSeq,
+ /* nameOverride */ proto.displayInfo?.name ?? null
+ );
+ const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0;
+ const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0;
+ const appRectWidth = proto.displayInfo?.appWidth ?? 0;
+ const appRectHeight = proto.displayInfo?.appHeight ?? 0;
+ const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null;
+ const movementBounds = proto.pinnedStackController?.movementBounds ?? null;
+
+ const entry = new DisplayContent(
+ proto.id,
+ proto.focusedRootTaskId,
+ proto.resumedActivity?.title ?? '',
+ proto.singleTaskInstance,
+ toRect(defaultBounds),
+ toRect(movementBounds),
+ new Rect(0, 0, displayRectWidth, displayRectHeight),
+ new Rect(0, 0, appRectWidth, appRectHeight),
+ proto.dpi,
+ proto.displayInfo?.flags ?? 0,
+ toRect(proto.displayFrames?.stableBounds),
+ proto.surfaceSize,
+ proto.focusedApp,
+ proto.appTransition?.lastUsedAppTransition ?? '',
+ proto.appTransition?.appTransitionState ?? '',
+ Rotation.Companion.getByValue(proto.displayRotation?.rotation ?? 0),
+ proto.displayRotation?.lastOrientation ?? 0,
+ createDisplayCutout(proto.displayInfo?.cutout),
+ windowContainer
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+ }
+};
+
+function createDisplayCutout(proto: any | null): DisplayCutout | null {
+ if (proto == null) {
+ return null;
+ } else {
+ return new DisplayCutout(
+ toInsets(proto?.insets),
+ toRect(proto?.boundLeft),
+ toRect(proto?.boundTop),
+ toRect(proto?.boundRight),
+ toRect(proto?.boundBottom),
+ toInsets(proto?.waterfallInsets)
+ );
+ }
+}
+
+function addAttributes(entry: DisplayContent, proto: any) {
+ entry.proto = proto;
+ entry.kind = entry.constructor.name;
+ entry.shortName = shortenName(entry.name);
+}
+
+export {DisplayContent};
diff --git a/tools/winscope/src/trace/flickerlib/windows/Task.ts b/tools/winscope/src/trace/flickerlib/windows/Task.ts
new file mode 100644
index 0000000..f5e4878
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/Task.ts
@@ -0,0 +1,67 @@
+/*
+ * 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, toRect} from '../common';
+import {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+Task.fromProto = (proto: any, isActivityInTree: boolean, nextSeq: () => number): Task => {
+ if (proto == null) {
+ return null;
+ } else {
+ const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer;
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ windowContainerProto,
+ /* protoChildren */ windowContainerProto?.children ?? [],
+ /* isActivityInTree */ isActivityInTree,
+ /* computedZ */ nextSeq
+ );
+
+ const entry = new Task(
+ proto.taskFragment?.activityType ?? proto.activityType,
+ proto.fillsParent,
+ toRect(proto.bounds),
+ proto.id,
+ proto.rootTaskId,
+ proto.taskFragment?.displayId,
+ toRect(proto.lastNonFullscreenBounds),
+ proto.realActivity,
+ proto.origActivity,
+ proto.resizeMode,
+ proto.resumedActivity?.title ?? '',
+ proto.animatingBounds,
+ proto.surfaceWidth,
+ proto.surfaceHeight,
+ proto.createdByOrganizer,
+ proto.taskFragment?.minWidth ?? proto.minWidth,
+ proto.taskFragment?.minHeight ?? proto.minHeight,
+ windowContainer
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+ }
+};
+
+function addAttributes(entry: Task, proto: any) {
+ entry.proto = proto;
+ entry.proto.configurationContainer = proto.windowContainer?.configurationContainer;
+ entry.proto.surfaceControl = proto.windowContainer?.surfaceControl;
+ entry.kind = entry.constructor.name;
+ entry.shortName = shortenName(entry.name);
+}
+
+export {Task};
diff --git a/tools/winscope/src/trace/flickerlib/windows/TaskFragment.ts b/tools/winscope/src/trace/flickerlib/windows/TaskFragment.ts
new file mode 100644
index 0000000..b6613af
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/TaskFragment.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021, 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 {TaskFragment} from '../common';
+import {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+TaskFragment.fromProto = (
+ proto: any,
+ isActivityInTree: boolean,
+ nextSeq: () => number
+): TaskFragment => {
+ if (proto == null) {
+ return null;
+ } else {
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.windowContainer,
+ /* protoChildren */ proto.windowContainer?.children ?? [],
+ /* isActivityInTree */ isActivityInTree,
+ /* computedZ */ nextSeq
+ );
+ const entry = new TaskFragment(
+ proto.activityType,
+ proto.displayId,
+ proto.minWidth,
+ proto.minHeight,
+ windowContainer
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+ }
+};
+
+function addAttributes(entry: TaskFragment, proto: any) {
+ entry.proto = proto;
+ entry.proto.configurationContainer = proto.windowContainer?.configurationContainer;
+ entry.proto.surfaceControl = proto.windowContainer?.surfaceControl;
+ entry.kind = entry.constructor.name;
+ entry.shortName = shortenName(entry.name);
+}
+
+export {TaskFragment};
diff --git a/tools/winscope/src/trace/flickerlib/windows/WindowContainer.ts b/tools/winscope/src/trace/flickerlib/windows/WindowContainer.ts
new file mode 100644
index 0000000..60c8d59
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/WindowContainer.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 {shortenName} from '../mixin';
+
+import {
+ Configuration,
+ ConfigurationContainer,
+ toRect,
+ WindowConfiguration,
+ WindowContainer,
+} from '../common';
+
+import {Activity} from './Activity';
+import {DisplayArea} from './DisplayArea';
+import {DisplayContent} from './DisplayContent';
+import {Task} from './Task';
+import {TaskFragment} from './TaskFragment';
+import {WindowState} from './WindowState';
+import {WindowToken} from './WindowToken';
+
+WindowContainer.fromProto = (
+ proto: any,
+ protoChildren: any[],
+ isActivityInTree: boolean,
+ nextSeq: () => number,
+ nameOverride: string | null = null,
+ identifierOverride: string | null = null,
+ tokenOverride: any = null
+): WindowContainer => {
+ if (proto == null) {
+ return null;
+ }
+
+ const containerOrder = nextSeq();
+ const children = protoChildren
+ .filter((it) => it != null)
+ .map((it) => WindowContainer.childrenFromProto(it, isActivityInTree, nextSeq))
+ .filter((it) => it != null);
+
+ const identifier: any = identifierOverride ?? proto.identifier;
+ const name: string = nameOverride ?? identifier?.title ?? '';
+ const token: string = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? '';
+
+ const config = createConfigurationContainer(proto.configurationContainer);
+ const entry = new WindowContainer(
+ name,
+ token,
+ proto.orientation,
+ proto.surfaceControl?.layerId ?? 0,
+ proto.visible,
+ config,
+ children,
+ containerOrder
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+};
+
+function addAttributes(entry: WindowContainer, proto: any) {
+ entry.proto = proto;
+ entry.kind = entry.constructor.name;
+ entry.shortName = shortenName(entry.name);
+}
+
+type WindowContainerChildType =
+ | DisplayContent
+ | DisplayArea
+ | Task
+ | TaskFragment
+ | Activity
+ | WindowToken
+ | WindowState
+ | WindowContainer;
+
+WindowContainer.childrenFromProto = (
+ proto: any,
+ isActivityInTree: boolean,
+ nextSeq: () => number
+): WindowContainerChildType => {
+ return (
+ DisplayContent.fromProto(proto.displayContent, isActivityInTree, nextSeq) ??
+ DisplayArea.fromProto(proto.displayArea, isActivityInTree, nextSeq) ??
+ Task.fromProto(proto.task, isActivityInTree, nextSeq) ??
+ TaskFragment.fromProto(proto.taskFragment, isActivityInTree, nextSeq) ??
+ Activity.fromProto(proto.activity, nextSeq) ??
+ WindowToken.fromProto(proto.windowToken, isActivityInTree, nextSeq) ??
+ WindowState.fromProto(proto.window, isActivityInTree, nextSeq) ??
+ WindowContainer.fromProto(proto.windowContainer, nextSeq)
+ );
+};
+
+function createConfigurationContainer(proto: any): ConfigurationContainer {
+ const entry = new ConfigurationContainer(
+ createConfiguration(proto?.overrideConfiguration ?? null),
+ createConfiguration(proto?.fullConfiguration ?? null),
+ createConfiguration(proto?.mergedOverrideConfiguration ?? null)
+ );
+
+ entry.obj = entry;
+ return entry;
+}
+
+function createConfiguration(proto: any): Configuration {
+ if (proto == null) {
+ return null;
+ }
+ let windowConfiguration = null;
+
+ if (proto != null && proto.windowConfiguration != null) {
+ windowConfiguration = createWindowConfiguration(proto.windowConfiguration);
+ }
+
+ return new Configuration(
+ windowConfiguration,
+ proto?.densityDpi ?? 0,
+ proto?.orientation ?? 0,
+ proto?.screenHeightDp ?? 0,
+ proto?.screenHeightDp ?? 0,
+ proto?.smallestScreenWidthDp ?? 0,
+ proto?.screenLayout ?? 0,
+ proto?.uiMode ?? 0
+ );
+}
+
+function createWindowConfiguration(proto: any): WindowConfiguration {
+ return new WindowConfiguration(
+ toRect(proto.appBounds),
+ toRect(proto.bounds),
+ toRect(proto.maxBounds),
+ proto.windowingMode,
+ proto.activityType
+ );
+}
+
+export {WindowContainer};
diff --git a/tools/winscope/src/trace/flickerlib/windows/WindowManagerState.ts b/tools/winscope/src/trace/flickerlib/windows/WindowManagerState.ts
new file mode 100644
index 0000000..d7e6d8c
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/WindowManagerState.ts
@@ -0,0 +1,136 @@
+/*
+ * 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 {TimeUtils} from 'common/time_utils';
+import {ElapsedTimestamp, RealTimestamp} from 'trace/timestamp';
+import {
+ KeyguardControllerState,
+ RootWindowContainer,
+ Rotation,
+ WindowManagerPolicy,
+ WindowManagerState,
+ WindowManagerTraceEntryBuilder,
+} from '../common';
+
+import {WindowContainer} from './WindowContainer';
+
+WindowManagerState.fromProto = (
+ proto: any,
+ elapsedTimestamp: bigint = 0n,
+ where: string = '',
+ realToElapsedTimeOffsetNs: bigint | undefined = undefined,
+ useElapsedTime = false
+): WindowManagerState => {
+ const inputMethodWIndowAppToken = '';
+ if (proto.inputMethodWindow != null) {
+ proto.inputMethodWindow.hashCode.toString(16);
+ }
+
+ let parseOrder = 0;
+ const nextSeq = () => ++parseOrder;
+ const rootWindowContainer = createRootWindowContainer(proto.rootWindowContainer, nextSeq);
+ const keyguardControllerState = createKeyguardControllerState(
+ proto.rootWindowContainer.keyguardController
+ );
+ const policy = createWindowManagerPolicy(proto.policy);
+
+ const entry = new WindowManagerTraceEntryBuilder(
+ `${elapsedTimestamp}`,
+ policy,
+ proto.focusedApp,
+ proto.focusedDisplayId,
+ proto.focusedWindow?.title ?? '',
+ inputMethodWIndowAppToken,
+ proto.rootWindowContainer.isHomeRecentsComponent,
+ proto.displayFrozen,
+ proto.rootWindowContainer.pendingActivities.map((it: any) => it.title),
+ rootWindowContainer,
+ keyguardControllerState,
+ where,
+ `${realToElapsedTimeOffsetNs ?? 0}`
+ ).build();
+
+ addAttributes(entry, proto, realToElapsedTimeOffsetNs === undefined || useElapsedTime);
+ return entry;
+};
+
+function addAttributes(entry: WindowManagerState, proto: any, useElapsedTime = false) {
+ entry.kind = entry.constructor.name;
+ if (!entry.isComplete()) {
+ entry.isIncompleteReason = entry.getIsIncompleteReason();
+ }
+ entry.proto = proto;
+ if (useElapsedTime || entry.clockTimestamp === undefined) {
+ entry.name = TimeUtils.format(new ElapsedTimestamp(BigInt(entry.elapsedTimestamp)));
+ entry.shortName = entry.name;
+ } else {
+ entry.name = TimeUtils.format(new RealTimestamp(BigInt(entry.clockTimestamp)));
+ entry.shortName = entry.name;
+ }
+}
+
+function createWindowManagerPolicy(proto: any): WindowManagerPolicy {
+ return new WindowManagerPolicy(
+ proto.focusedAppToken ?? '',
+ proto.forceStatusBar,
+ proto.forceStatusBarFromKeyguard,
+ proto.keyguardDrawComplete,
+ proto.keyguardOccluded,
+ proto.keyguardOccludedChanged,
+ proto.keyguardOccludedPending,
+ proto.lastSystemUiFlags,
+ proto.orientation,
+ Rotation.Companion.getByValue(proto.rotation),
+ proto.rotationMode,
+ proto.screenOnFully,
+ proto.windowManagerDrawComplete
+ );
+}
+
+function createRootWindowContainer(proto: any, nextSeq: () => number): RootWindowContainer {
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.windowContainer,
+ /* childrenProto */ proto.windowContainer?.children ?? [],
+ /* isActivityInTree */ false,
+ /* computedZ */ nextSeq
+ );
+
+ if (windowContainer == null) {
+ throw new Error(`Window container should not be null.\n${JSON.stringify(proto)}`);
+ }
+ const entry = new RootWindowContainer(windowContainer);
+ return entry;
+}
+
+function createKeyguardControllerState(proto: any): KeyguardControllerState {
+ const keyguardOccludedStates: any = {};
+
+ if (proto) {
+ proto.keyguardOccludedStates.forEach(
+ (it: any) =>
+ (keyguardOccludedStates[it.displayId as keyof typeof keyguardOccludedStates] =
+ it.keyguardOccluded)
+ );
+ }
+
+ return new KeyguardControllerState(
+ proto?.isAodShowing ?? false,
+ proto?.isKeyguardShowing ?? false,
+ keyguardOccludedStates
+ );
+}
+
+export {WindowManagerState};
diff --git a/tools/winscope/src/trace/flickerlib/windows/WindowState.ts b/tools/winscope/src/trace/flickerlib/windows/WindowState.ts
new file mode 100644
index 0000000..3d47c4b
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/WindowState.ts
@@ -0,0 +1,142 @@
+/*
+ * 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 {Size, toRect, WindowLayoutParams, WindowState} from '../common';
+import {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+WindowState.fromProto = (
+ proto: any,
+ isActivityInTree: boolean,
+ nextSeq: () => number
+): WindowState => {
+ if (proto == null) {
+ return null;
+ } else {
+ const windowParams = createWindowLayoutParams(proto.attributes);
+ const identifierName = getIdentifier(proto);
+ const windowType = getWindowType(proto, identifierName);
+ const name = getName(identifierName);
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.windowContainer,
+ /* protoChildren */ proto.windowContainer?.children ?? [],
+ /* isActivityInTree */ isActivityInTree,
+ /* computedZ */ nextSeq,
+ /* nameOverride */ name,
+ /* identifierOverride */ proto.identifier
+ );
+
+ const entry = new WindowState(
+ windowParams,
+ proto.displayId,
+ proto.stackId,
+ proto.animator?.surface?.layer ?? 0,
+ proto.animator?.surface?.shown ?? false,
+ windowType,
+ new Size(proto.requestedWidth, proto.requestedHeight),
+ toRect(proto.surfacePosition),
+ toRect(proto.windowFrames?.frame ?? null),
+ toRect(proto.windowFrames?.containingFrame ?? null),
+ toRect(proto.windowFrames?.parentFrame ?? null),
+ toRect(proto.windowFrames?.contentFrame ?? null),
+ toRect(proto.windowFrames?.contentInsets ?? null),
+ toRect(proto.surfaceInsets),
+ toRect(proto.givenContentInsets),
+ toRect(proto.animator?.lastClipRect ?? null),
+ windowContainer,
+ /* isAppWindow */ isActivityInTree
+ );
+
+ addAttributes(entry, proto);
+ return entry;
+ }
+};
+
+function createWindowLayoutParams(proto: any): WindowLayoutParams {
+ return new WindowLayoutParams(
+ /* type */ proto?.type ?? 0,
+ /* x */ proto?.x ?? 0,
+ /* y */ proto?.y ?? 0,
+ /* width */ proto?.width ?? 0,
+ /* height */ proto?.height ?? 0,
+ /* horizontalMargin */ proto?.horizontalMargin ?? 0,
+ /* verticalMargin */ proto?.verticalMargin ?? 0,
+ /* gravity */ proto?.gravity ?? 0,
+ /* softInputMode */ proto?.softInputMode ?? 0,
+ /* format */ proto?.format ?? 0,
+ /* windowAnimations */ proto?.windowAnimations ?? 0,
+ /* alpha */ proto?.alpha ?? 0,
+ /* screenBrightness */ proto?.screenBrightness ?? 0,
+ /* buttonBrightness */ proto?.buttonBrightness ?? 0,
+ /* rotationAnimation */ proto?.rotationAnimation ?? 0,
+ /* preferredRefreshRate */ proto?.preferredRefreshRate ?? 0,
+ /* preferredDisplayModeId */ proto?.preferredDisplayModeId ?? 0,
+ /* hasSystemUiListeners */ proto?.hasSystemUiListeners ?? false,
+ /* inputFeatureFlags */ proto?.inputFeatureFlags ?? 0,
+ /* userActivityTimeout */ proto?.userActivityTimeout ?? 0,
+ /* colorMode */ proto?.colorMode ?? 0,
+ /* flags */ proto?.flags ?? 0,
+ /* privateFlags */ proto?.privateFlags ?? 0,
+ /* systemUiVisibilityFlags */ proto?.systemUiVisibilityFlags ?? 0,
+ /* subtreeSystemUiVisibilityFlags */ proto?.subtreeSystemUiVisibilityFlags ?? 0,
+ /* appearance */ proto?.appearance ?? 0,
+ /* behavior */ proto?.behavior ?? 0,
+ /* fitInsetsTypes */ proto?.fitInsetsTypes ?? 0,
+ /* fitInsetsSides */ proto?.fitInsetsSides ?? 0,
+ /* fitIgnoreVisibility */ proto?.fitIgnoreVisibility ?? false
+ );
+}
+
+function getWindowType(proto: any, identifierName: string): number {
+ if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
+ return WindowState.WINDOW_TYPE_STARTING;
+ } else if (proto.animatingExit) {
+ return WindowState.WINDOW_TYPE_EXITING;
+ } else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
+ return WindowState.WINDOW_TYPE_STARTING;
+ }
+
+ return 0;
+}
+
+function getName(identifierName: string): string {
+ let name = identifierName;
+
+ if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
+ name = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length);
+ } else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
+ name = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length);
+ }
+
+ return name;
+}
+
+function getIdentifier(proto: any): string {
+ return proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? '';
+}
+
+function addAttributes(entry: WindowState, proto: any) {
+ entry.kind = entry.constructor.name;
+ entry.rect = entry.frame;
+ entry.rect.ref = entry;
+ entry.rect.label = entry.name;
+ entry.proto = proto;
+ entry.proto.configurationContainer = proto.windowContainer?.configurationContainer;
+ entry.proto.surfaceControl = proto.windowContainer?.surfaceControl;
+ entry.shortName = shortenName(entry.name);
+}
+
+export {WindowState};
diff --git a/tools/winscope/src/trace/flickerlib/windows/WindowToken.ts b/tools/winscope/src/trace/flickerlib/windows/WindowToken.ts
new file mode 100644
index 0000000..5c70122
--- /dev/null
+++ b/tools/winscope/src/trace/flickerlib/windows/WindowToken.ts
@@ -0,0 +1,48 @@
+/*
+ * 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 {shortenName} from '../mixin';
+import {WindowContainer} from './WindowContainer';
+
+WindowToken.fromProto = (
+ proto: any,
+ isActivityInTree: boolean,
+ nextSeq: () => number
+): WindowToken => {
+ if (!proto) {
+ return null;
+ }
+
+ const windowContainer = WindowContainer.fromProto(
+ /* proto */ proto.windowContainer,
+ /* protoChildren */ proto.windowContainer?.children ?? [],
+ /* isActivityInTree */ isActivityInTree,
+ /* computedZ */ nextSeq,
+ /* nameOverride */ proto.hashCode.toString(16),
+ /* identifierOverride */ null,
+ /* tokenOverride */ proto.hashCode
+ );
+ const entry = new WindowToken(windowContainer);
+ entry.kind = entry.constructor.name;
+ entry.proto = proto;
+ entry.proto.configurationContainer = proto.windowContainer?.configurationContainer;
+ entry.proto.surfaceControl = proto.windowContainer?.surfaceControl;
+ entry.shortName = shortenName(entry.name);
+ return entry;
+};
+
+export {WindowToken};
diff --git a/tools/winscope/src/trace/frame_map.ts b/tools/winscope/src/trace/frame_map.ts
new file mode 100644
index 0000000..3685582
--- /dev/null
+++ b/tools/winscope/src/trace/frame_map.ts
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 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 {AbsoluteEntryIndex, AbsoluteFrameIndex, EntriesRange, FramesRange} from './index_types';
+
+export class FrameMap {
+ readonly lengthEntries: number;
+ readonly lengthFrames: number;
+
+ // These lookup tables allow to convert a "[start_entry; end_entry[" range
+ // to "[start_frame; end_frame[" in O(1) time.
+ //
+ // entryToStartFrame[i] is:
+ // - start_frame of entry_i
+ // - start_frame of the first entry_j (j > i), if entry_i has no associated frames
+ // - undefined, if all the entries with index >= i have no associated frames
+ //
+ // entryToEndFrame[i] is:
+ // - end_frame of entry_i
+ // - end_frame of the last entry_j (j < i), if entry_i has no associated frames
+ // - undefined, if all the entries with index <= i have no associated frames
+ private readonly entryToStartFrame: Array<AbsoluteFrameIndex | undefined>;
+ private readonly entryToEndFrame: Array<AbsoluteFrameIndex | undefined>;
+
+ // These lookup tables allow to convert a "[start_frame; end_frame[" range
+ // to "[start_entry; end_entry[" in O(1) time.
+ //
+ // frameToStartEntry[i] is:
+ // - start_entry of frame_i
+ // - start_entry of the first frame_j (j > i), if frame_i has no associated entries
+ // - undefined, if all the frames with index >= i have no associated entries
+ //
+ // frameToEndEntry[i] is:
+ // - end_entry of frame_i
+ // - end_entry of the last frame_j (j < i), if frame_i has no associated entries
+ // - undefined, if all the frames with index <= i have no associated entries
+ private readonly frameToStartEntry: Array<AbsoluteEntryIndex | undefined>;
+ private readonly frameToEndEntry: Array<AbsoluteEntryIndex | undefined>;
+
+ constructor(
+ lengthEntries: number,
+ lengthFrames: number,
+ entryToStartFrame: Array<AbsoluteFrameIndex | undefined>,
+ entryToEndFrame: Array<AbsoluteFrameIndex | undefined>,
+ frameToStartEntry: Array<AbsoluteEntryIndex | undefined>,
+ frameToEndEntry: Array<AbsoluteEntryIndex | undefined>
+ ) {
+ this.lengthEntries = lengthEntries;
+ this.lengthFrames = lengthFrames;
+ this.entryToStartFrame = entryToStartFrame;
+ this.entryToEndFrame = entryToEndFrame;
+ this.frameToStartEntry = frameToStartEntry;
+ this.frameToEndEntry = frameToEndEntry;
+ }
+
+ getFramesRange(entries: EntriesRange): FramesRange | undefined {
+ entries = this.clampEntriesRangeToFitBounds(entries);
+ if (entries.start >= entries.end) {
+ return undefined;
+ }
+
+ const startFrame = this.getStartFrameOfFirstGreaterOrEqualMappedEntry(entries.start);
+ const endFrame = this.getEndFrameOfLastLowerOrEqualMappedEntry(entries.end - 1);
+
+ if (startFrame === undefined || endFrame === undefined || startFrame >= endFrame) {
+ return undefined;
+ }
+
+ return {start: startFrame, end: endFrame};
+ }
+
+ getFullTraceFramesRange(): FramesRange | undefined {
+ return this.getFramesRange({start: 0, end: this.lengthEntries});
+ }
+
+ getEntriesRange(frames: FramesRange): EntriesRange | undefined {
+ frames = this.clampFramesRangeToFitBounds(frames);
+ if (frames.start >= frames.end) {
+ return undefined;
+ }
+
+ const startEntry = this.getStartEntryOfFirstGreaterOrEqualMappedFrame(frames.start);
+ const endEntry = this.getEndEntryOfLastLowerOrEqualMappedFrame(frames.end - 1);
+
+ if (startEntry === undefined || endEntry === undefined || startEntry >= endEntry) {
+ return undefined;
+ }
+
+ return {start: startEntry, end: endEntry};
+ }
+
+ private getStartFrameOfFirstGreaterOrEqualMappedEntry(
+ entry: AbsoluteEntryIndex
+ ): AbsoluteFrameIndex | undefined {
+ if (entry < 0 || entry >= this.lengthEntries) {
+ throw Error(`Entry index out of bounds: ${entry}`);
+ }
+ return this.entryToStartFrame[entry];
+ }
+
+ private getEndFrameOfLastLowerOrEqualMappedEntry(
+ entry: AbsoluteEntryIndex
+ ): AbsoluteFrameIndex | undefined {
+ if (entry < 0 || entry >= this.lengthEntries) {
+ throw Error(`Entry index out of bounds: ${entry}`);
+ }
+ return this.entryToEndFrame[entry];
+ }
+
+ private getStartEntryOfFirstGreaterOrEqualMappedFrame(
+ frame: AbsoluteFrameIndex
+ ): AbsoluteEntryIndex | undefined {
+ if (frame < 0 || frame >= this.lengthFrames) {
+ throw Error(`Frame index out of bounds: ${frame}`);
+ }
+ return this.frameToStartEntry[frame];
+ }
+
+ private getEndEntryOfLastLowerOrEqualMappedFrame(
+ frame: AbsoluteFrameIndex
+ ): AbsoluteEntryIndex | undefined {
+ if (frame < 0 || frame >= this.lengthFrames) {
+ throw Error(`Frame index out of bounds: ${frame}`);
+ }
+ return this.frameToEndEntry[frame];
+ }
+
+ private clampEntriesRangeToFitBounds(entries: EntriesRange): EntriesRange {
+ return {
+ start: Math.max(entries.start, 0),
+ end: Math.min(entries.end, this.lengthEntries),
+ };
+ }
+
+ private clampFramesRangeToFitBounds(frames: FramesRange): FramesRange {
+ return {
+ start: Math.max(frames.start, 0),
+ end: Math.min(frames.end, this.lengthFrames),
+ };
+ }
+}
diff --git a/tools/winscope/src/trace/frame_map_builder.ts b/tools/winscope/src/trace/frame_map_builder.ts
new file mode 100644
index 0000000..dd0781d
--- /dev/null
+++ b/tools/winscope/src/trace/frame_map_builder.ts
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 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 {FrameMap} from './frame_map';
+import {AbsoluteEntryIndex, AbsoluteFrameIndex, FramesRange} from './index_types';
+
+export class FrameMapBuilder {
+ private readonly lengthEntries: number;
+ private readonly lengthFrames: number;
+
+ // See comments in FrameMap about the semantics of these lookup tables
+ private readonly entryToStartFrame: Array<AbsoluteFrameIndex | undefined>;
+ private readonly entryToEndFrame: Array<AbsoluteFrameIndex | undefined>;
+ private readonly frameToStartEntry: Array<AbsoluteEntryIndex | undefined>;
+ private readonly frameToEndEntry: Array<AbsoluteEntryIndex | undefined>;
+
+ private isFinalized = false;
+
+ constructor(lengthEntries: number, lengthFrames: number) {
+ this.lengthEntries = lengthEntries;
+ this.lengthFrames = lengthFrames;
+
+ this.entryToStartFrame = new Array<AbsoluteFrameIndex | undefined>(this.lengthEntries).fill(
+ undefined
+ );
+ this.entryToEndFrame = new Array<AbsoluteFrameIndex | undefined>(this.lengthEntries).fill(
+ undefined
+ );
+ this.frameToStartEntry = new Array<AbsoluteEntryIndex | undefined>(this.lengthFrames).fill(
+ undefined
+ );
+ this.frameToEndEntry = new Array<AbsoluteEntryIndex | undefined>(this.lengthFrames).fill(
+ undefined
+ );
+ }
+
+ setFrames(entry: AbsoluteEntryIndex, range: FramesRange | undefined): FrameMapBuilder {
+ this.checkIsNotFinalized();
+ if (!range || range.start === range.end) {
+ return this;
+ }
+
+ this.setStartArrayValue(this.entryToStartFrame, entry, range.start);
+ this.setEndArrayValue(this.entryToEndFrame, entry, range.end);
+
+ for (let frame = range.start; frame < range.end; ++frame) {
+ this.setStartArrayValue(this.frameToStartEntry, frame, entry);
+ this.setEndArrayValue(this.frameToEndEntry, frame, entry + 1);
+ }
+
+ return this;
+ }
+
+ build(): FrameMap {
+ this.checkIsNotFinalized();
+ this.finalizeStartArray(this.entryToStartFrame);
+ this.finalizeEndArray(this.entryToEndFrame);
+ this.finalizeStartArray(this.frameToStartEntry);
+ this.finalizeEndArray(this.frameToEndEntry);
+ this.isFinalized = true;
+ return new FrameMap(
+ this.lengthEntries,
+ this.lengthFrames,
+ this.entryToStartFrame,
+ this.entryToEndFrame,
+ this.frameToStartEntry,
+ this.frameToEndEntry
+ );
+ }
+
+ private setStartArrayValue(array: Array<number | undefined>, index: number, value: number) {
+ const currentValue = array[index];
+ if (currentValue === undefined) {
+ array[index] = value;
+ } else {
+ array[index] = Math.min(currentValue, value);
+ }
+ }
+
+ private setEndArrayValue(array: Array<number | undefined>, index: number, value: number) {
+ const currentValue = array[index];
+ if (currentValue === undefined) {
+ array[index] = value;
+ } else {
+ array[index] = Math.max(currentValue, value);
+ }
+ }
+
+ private finalizeStartArray(array: Array<number | undefined>) {
+ let firstValidStart: number | undefined = undefined;
+ for (let i = array.length - 1; i >= 0; --i) {
+ if (array[i] === undefined) {
+ array[i] = firstValidStart;
+ } else {
+ firstValidStart = array[i];
+ }
+ }
+ }
+
+ private finalizeEndArray(array: Array<number | undefined>) {
+ let lastValidEnd: number | undefined = undefined;
+ for (let i = 0; i < array.length; ++i) {
+ if (array[i] === undefined) {
+ array[i] = lastValidEnd;
+ } else {
+ lastValidEnd = array[i];
+ }
+ }
+ }
+
+ private checkIsNotFinalized() {
+ if (this.isFinalized) {
+ throw new Error('Attemped to modify already finalized frame map.');
+ }
+ }
+}
diff --git a/tools/winscope/src/trace/frame_map_test.ts b/tools/winscope/src/trace/frame_map_test.ts
new file mode 100644
index 0000000..248a43c
--- /dev/null
+++ b/tools/winscope/src/trace/frame_map_test.ts
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 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 {FrameMap} from './frame_map';
+import {FrameMapBuilder} from './frame_map_builder';
+
+describe('FrameMapTest', () => {
+ let map: FrameMap;
+
+ beforeAll(() => {
+ // Entry: 0 1-2 3 4 5
+ // | |
+ // | |
+ // Frame: 1 2 3 4-5-6
+ map = new FrameMapBuilder(5, 7)
+ .setFrames(1, {start: 1, end: 2})
+ .setFrames(2, {start: 1, end: 2})
+ .setFrames(4, {start: 4, end: 7})
+ .build();
+ });
+
+ it('getFramesRange()', () => {
+ // empty
+ expect(map.getFramesRange({start: -2, end: -1})).toEqual(undefined);
+ expect(map.getFramesRange({start: 0, end: 1})).toEqual(undefined);
+ expect(map.getFramesRange({start: 5, end: 6})).toEqual(undefined);
+ expect(map.getFramesRange({start: 1, end: 1})).toEqual(undefined);
+
+ // full
+ expect(map.getFramesRange({start: 0, end: 6})).toEqual({start: 1, end: 7});
+ expect(map.getFramesRange({start: 1, end: 5})).toEqual({start: 1, end: 7});
+
+ // middle
+ expect(map.getFramesRange({start: 1, end: 4})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 1, end: 2})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 2, end: 3})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 3, end: 4})).toEqual(undefined);
+ expect(map.getFramesRange({start: 4, end: 5})).toEqual({start: 4, end: 7});
+
+ // slice away front
+ expect(map.getFramesRange({start: 1, end: 6})).toEqual({start: 1, end: 7});
+ expect(map.getFramesRange({start: 2, end: 6})).toEqual({start: 1, end: 7});
+ expect(map.getFramesRange({start: 3, end: 6})).toEqual({start: 4, end: 7});
+ expect(map.getFramesRange({start: 4, end: 6})).toEqual({start: 4, end: 7});
+ expect(map.getFramesRange({start: 5, end: 6})).toEqual(undefined);
+ expect(map.getFramesRange({start: 6, end: 6})).toEqual(undefined);
+
+ // slice away back
+ expect(map.getFramesRange({start: 0, end: 5})).toEqual({start: 1, end: 7});
+ expect(map.getFramesRange({start: 0, end: 4})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 0, end: 3})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 0, end: 2})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 0, end: 1})).toEqual(undefined);
+ expect(map.getFramesRange({start: 0, end: 0})).toEqual(undefined);
+
+ // query out of bounds
+ expect(map.getFramesRange({start: -1, end: 7})).toEqual({start: 1, end: 7});
+ expect(map.getFramesRange({start: -10, end: 10})).toEqual({start: 1, end: 7});
+ expect(map.getFramesRange({start: -1, end: 4})).toEqual({start: 1, end: 2});
+ expect(map.getFramesRange({start: 4, end: 10})).toEqual({start: 4, end: 7});
+ });
+
+ it('getEntriesRange()', () => {
+ // empty
+ expect(map.getEntriesRange({start: -2, end: -1})).toEqual(undefined);
+ expect(map.getEntriesRange({start: 7, end: 8})).toEqual(undefined);
+ expect(map.getEntriesRange({start: 2, end: 4})).toEqual(undefined);
+ expect(map.getEntriesRange({start: 3, end: 2})).toEqual(undefined);
+ expect(map.getEntriesRange({start: 2, end: 2})).toEqual(undefined);
+
+ // full
+ expect(map.getEntriesRange({start: 0, end: 7})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: -1, end: 8})).toEqual({start: 1, end: 5});
+
+ // middle
+ expect(map.getEntriesRange({start: 1, end: 2})).toEqual({start: 1, end: 3});
+ expect(map.getEntriesRange({start: 6, end: 7})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 1, end: 5})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: 2, end: 5})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 1, end: 4})).toEqual({start: 1, end: 3});
+
+ // slice away front
+ expect(map.getEntriesRange({start: 0, end: 7})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: 1, end: 7})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: 2, end: 7})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 3, end: 7})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 4, end: 7})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 5, end: 7})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 6, end: 7})).toEqual({start: 4, end: 5});
+ expect(map.getEntriesRange({start: 7, end: 7})).toEqual(undefined);
+
+ // slice away back
+ expect(map.getEntriesRange({start: 1, end: 6})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: 1, end: 5})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: 1, end: 4})).toEqual({start: 1, end: 3});
+ expect(map.getEntriesRange({start: 1, end: 3})).toEqual({start: 1, end: 3});
+ expect(map.getEntriesRange({start: 1, end: 2})).toEqual({start: 1, end: 3});
+ expect(map.getEntriesRange({start: 1, end: 1})).toEqual(undefined);
+
+ // query out of bounds
+ expect(map.getEntriesRange({start: 0, end: 8})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: -10, end: 10})).toEqual({start: 1, end: 5});
+ expect(map.getEntriesRange({start: -10, end: 4})).toEqual({start: 1, end: 3});
+ expect(map.getEntriesRange({start: 2, end: 10})).toEqual({start: 4, end: 5});
+ });
+
+ it('getFullTraceFramesRange()', () => {
+ expect(map.getFullTraceFramesRange()).toEqual({start: 1, end: 7});
+ });
+});
diff --git a/tools/winscope/src/trace/frame_mapper.ts b/tools/winscope/src/trace/frame_mapper.ts
new file mode 100644
index 0000000..0da1261
--- /dev/null
+++ b/tools/winscope/src/trace/frame_mapper.ts
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2023 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 {assertDefined} from 'common/assert_utils';
+import {FrameMapBuilder} from './frame_map_builder';
+import {FramesRange, TraceEntry} from './trace';
+import {Traces} from './traces';
+import {TraceType} from './trace_type';
+
+export class FrameMapper {
+ // Value used to narrow time-based searches of corresponding trace entries
+ private static readonly MAX_UI_PIPELINE_LATENCY_NS = 2000000000n; // 2 seconds
+
+ constructor(private traces: Traces) {}
+
+ computeMapping() {
+ this.pickMostReliableTraceAndSetInitialFrameInfo();
+ this.propagateFrameInfoToOtherTraces();
+ }
+
+ private pickMostReliableTraceAndSetInitialFrameInfo() {
+ const TRACES_IN_PREFERENCE_ORDER = [
+ TraceType.SCREEN_RECORDING,
+ TraceType.SURFACE_FLINGER,
+ TraceType.WINDOW_MANAGER,
+ ];
+
+ const type = TRACES_IN_PREFERENCE_ORDER.find(
+ (type) => this.traces.getTrace(type) !== undefined
+ );
+ if (type === undefined) {
+ return;
+ }
+
+ const trace = assertDefined(this.traces.getTrace(type));
+ const frameMapBuilder = new FrameMapBuilder(trace.lengthEntries, trace.lengthEntries);
+
+ for (let i = 0; i < trace.lengthEntries; ++i) {
+ frameMapBuilder.setFrames(i, {start: i, end: i + 1});
+ }
+
+ const frameMap = frameMapBuilder.build();
+ trace.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ private propagateFrameInfoToOtherTraces() {
+ this.tryPropagateFromScreenRecordingToSurfaceFlinger();
+ this.tryPropagateFromSurfaceFlingerToTransactions();
+ this.tryPropagateFromTransactionsToWindowManager();
+ this.tryPropagateFromWindowManagerToProtoLog();
+ this.tryPropagateFromWindowManagerToIme();
+ }
+
+ private tryPropagateFromScreenRecordingToSurfaceFlinger() {
+ const frameMapBuilder = this.tryStartFrameMapping(
+ TraceType.SCREEN_RECORDING,
+ TraceType.SURFACE_FLINGER
+ );
+ if (!frameMapBuilder) {
+ return;
+ }
+
+ const screenRecording = assertDefined(this.traces.getTrace(TraceType.SCREEN_RECORDING));
+ const surfaceFlinger = assertDefined(this.traces.getTrace(TraceType.SURFACE_FLINGER));
+
+ screenRecording.forEachEntry((srcEntry) => {
+ const startSearchTime = srcEntry.getTimestamp().add(-FrameMapper.MAX_UI_PIPELINE_LATENCY_NS);
+ const endSearchTime = srcEntry.getTimestamp();
+ const matches = surfaceFlinger.sliceTime(startSearchTime, endSearchTime);
+ if (matches.lengthEntries > 0) {
+ const dstEntry = matches.getEntry(matches.lengthEntries - 1);
+ frameMapBuilder.setFrames(dstEntry.getIndex(), srcEntry.getFramesRange());
+ }
+ });
+
+ const frameMap = frameMapBuilder.build();
+ surfaceFlinger.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ private tryPropagateFromSurfaceFlingerToTransactions() {
+ const frameMapBuilder = this.tryStartFrameMapping(
+ TraceType.SURFACE_FLINGER,
+ TraceType.TRANSACTIONS
+ );
+ if (!frameMapBuilder) {
+ return;
+ }
+
+ const transactions = assertDefined(this.traces.getTrace(TraceType.TRANSACTIONS));
+ const surfaceFlinger = assertDefined(this.traces.getTrace(TraceType.SURFACE_FLINGER));
+
+ const vsyncIdToFrames = new Map<bigint, FramesRange>();
+ surfaceFlinger.forEachEntry((srcEntry) => {
+ const vsyncId = this.getVsyncIdProperty(srcEntry, 'vSyncId');
+ if (vsyncId === undefined) {
+ return;
+ }
+ const srcFrames = srcEntry.getFramesRange();
+ if (!srcFrames) {
+ return;
+ }
+ let frames = vsyncIdToFrames.get(vsyncId);
+ if (!frames) {
+ frames = {start: Number.MAX_VALUE, end: Number.MIN_VALUE};
+ }
+ frames.start = Math.min(frames.start, srcFrames.start);
+ frames.end = Math.max(frames.end, srcFrames.end);
+ vsyncIdToFrames.set(vsyncId, frames);
+ });
+
+ transactions.forEachEntry((dstEntry) => {
+ const vsyncId = this.getVsyncIdProperty(dstEntry, 'vsyncId');
+ if (vsyncId === undefined) {
+ return;
+ }
+ const frames = vsyncIdToFrames.get(vsyncId);
+ if (frames === undefined) {
+ return;
+ }
+ frameMapBuilder.setFrames(dstEntry.getIndex(), frames);
+ });
+
+ const frameMap = frameMapBuilder.build();
+ transactions.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ private tryPropagateFromTransactionsToWindowManager() {
+ const frameMapBuilder = this.tryStartFrameMapping(
+ TraceType.TRANSACTIONS,
+ TraceType.WINDOW_MANAGER
+ );
+ if (!frameMapBuilder) {
+ return;
+ }
+
+ const windowManager = assertDefined(this.traces.getTrace(TraceType.WINDOW_MANAGER));
+ const transactions = assertDefined(this.traces.getTrace(TraceType.TRANSACTIONS));
+
+ let prevWindowManagerEntry: TraceEntry<object> | undefined;
+ windowManager.forEachEntry((windowManagerEntry) => {
+ if (prevWindowManagerEntry) {
+ const matches = transactions.sliceTime(
+ prevWindowManagerEntry.getTimestamp(),
+ windowManagerEntry.getTimestamp()
+ );
+ frameMapBuilder.setFrames(prevWindowManagerEntry.getIndex(), matches.getFramesRange());
+ }
+ prevWindowManagerEntry = windowManagerEntry;
+ });
+
+ if (windowManager.lengthEntries > 0) {
+ const lastWindowManagerEntry = windowManager.getEntry(-1);
+ const startSearchTime = lastWindowManagerEntry.getTimestamp();
+ const endSearchTime = startSearchTime.add(FrameMapper.MAX_UI_PIPELINE_LATENCY_NS);
+ const matches = transactions.sliceTime(startSearchTime, endSearchTime);
+ frameMapBuilder.setFrames(lastWindowManagerEntry.getIndex(), matches.getFramesRange());
+ }
+
+ const frameMap = frameMapBuilder.build();
+ windowManager.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ private tryPropagateFromWindowManagerToProtoLog() {
+ const frameMapBuilder = this.tryStartFrameMapping(
+ TraceType.WINDOW_MANAGER,
+ TraceType.PROTO_LOG
+ );
+ if (!frameMapBuilder) {
+ return;
+ }
+
+ const protoLog = assertDefined(this.traces.getTrace(TraceType.PROTO_LOG));
+ const windowManager = assertDefined(this.traces.getTrace(TraceType.WINDOW_MANAGER));
+
+ windowManager.forEachEntry((prevSrcEntry) => {
+ const srcEntryIndex = prevSrcEntry.getIndex() + 1;
+ const srcEntry =
+ srcEntryIndex < windowManager.lengthEntries
+ ? windowManager.getEntry(srcEntryIndex)
+ : undefined;
+ if (srcEntry === undefined) {
+ return;
+ }
+ const startSearchTime = prevSrcEntry.getTimestamp().add(1n);
+ const endSearchTime = srcEntry.getTimestamp().add(1n);
+ const matches = protoLog.sliceTime(startSearchTime, endSearchTime);
+ matches.forEachEntry((dstEntry) => {
+ frameMapBuilder.setFrames(dstEntry.getIndex(), srcEntry.getFramesRange());
+ });
+ });
+
+ if (windowManager.lengthEntries > 0) {
+ const firstEntry = windowManager.getEntry(0);
+ const startSearchTime = firstEntry
+ .getTimestamp()
+ .add(-FrameMapper.MAX_UI_PIPELINE_LATENCY_NS);
+ const endSearchTime = firstEntry.getTimestamp().add(1n);
+ const matches = protoLog.sliceTime(startSearchTime, endSearchTime);
+ matches.forEachEntry((dstEntry) => {
+ frameMapBuilder.setFrames(dstEntry.getIndex(), firstEntry.getFramesRange());
+ });
+ }
+
+ const frameMap = frameMapBuilder.build();
+ protoLog.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ private tryPropagateFromWindowManagerToIme() {
+ const imeTypes = [
+ TraceType.INPUT_METHOD_CLIENTS,
+ TraceType.INPUT_METHOD_MANAGER_SERVICE,
+ TraceType.INPUT_METHOD_SERVICE,
+ ];
+ for (const imeType of imeTypes) {
+ const frameMapBuilder = this.tryStartFrameMapping(TraceType.WINDOW_MANAGER, imeType);
+ if (frameMapBuilder) {
+ this.propagateFromWindowManagerToIme(imeType, frameMapBuilder);
+ }
+ }
+ }
+
+ private propagateFromWindowManagerToIme(
+ imeTraceType: TraceType,
+ frameMapBuilder: FrameMapBuilder
+ ) {
+ // Value used to narrow time-based searches of corresponding WindowManager entries
+ const MAX_TIME_DIFFERENCE_NS = 200000000n; // 200 ms
+
+ const ime = assertDefined(this.traces.getTrace(imeTraceType));
+ const windowManager = assertDefined(this.traces.getTrace(TraceType.WINDOW_MANAGER));
+ const abs = (n: bigint): bigint => (n < 0n ? -n : n);
+
+ ime.forEachEntry((dstEntry) => {
+ const srcEntry = windowManager.findClosestEntry(dstEntry.getTimestamp());
+ if (!srcEntry) {
+ return;
+ }
+ const timeDifferenceNs = abs(
+ srcEntry.getTimestamp().getValueNs() - dstEntry.getTimestamp().getValueNs()
+ );
+ if (timeDifferenceNs > MAX_TIME_DIFFERENCE_NS) {
+ return;
+ }
+ frameMapBuilder.setFrames(dstEntry.getIndex(), srcEntry.getFramesRange());
+ });
+
+ const frameMap = frameMapBuilder.build();
+ ime.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
+ }
+
+ private tryStartFrameMapping(
+ srcTraceType: TraceType,
+ dstTraceType: TraceType
+ ): FrameMapBuilder | undefined {
+ const srcTrace = this.traces.getTrace(srcTraceType);
+ const dstTrace = this.traces.getTrace(dstTraceType);
+ if (!srcTrace || !dstTrace || !srcTrace.hasFrameInfo()) {
+ return undefined;
+ }
+
+ const framesRange = srcTrace.getFramesRange();
+ const lengthFrames = framesRange ? framesRange.end : 0;
+ return new FrameMapBuilder(dstTrace.lengthEntries, lengthFrames);
+ }
+
+ private getVsyncIdProperty(entry: TraceEntry<object>, propertyKey: string): bigint | undefined {
+ const entryValue = entry.getValue();
+ const vsyncId = (entryValue as any)[propertyKey];
+ if (vsyncId === undefined) {
+ console.error(`Failed to get trace entry's '${propertyKey}' property:`, entryValue);
+ return undefined;
+ }
+ try {
+ return BigInt(vsyncId.toString());
+ } catch (e) {
+ console.error(`Failed to convert trace entry's vsyncId to bigint:`, entryValue);
+ return undefined;
+ }
+ }
+}
diff --git a/tools/winscope/src/trace/frame_mapper_test.ts b/tools/winscope/src/trace/frame_mapper_test.ts
new file mode 100644
index 0000000..93b60c7
--- /dev/null
+++ b/tools/winscope/src/trace/frame_mapper_test.ts
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2023 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 {TracesUtils} from 'test/unit/traces_utils';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry';
+import {WindowManagerState} from './flickerlib/windows/WindowManagerState';
+import {FrameMapper} from './frame_mapper';
+import {AbsoluteFrameIndex} from './index_types';
+import {LogMessage} from './protolog';
+import {ScreenRecordingTraceEntry} from './screen_recording';
+import {RealTimestamp} from './timestamp';
+import {Trace} from './trace';
+import {Traces} from './traces';
+import {TraceType} from './trace_type';
+
+describe('FrameMapper', () => {
+ const time0 = new RealTimestamp(0n);
+ const time1 = new RealTimestamp(1n);
+ const time2 = new RealTimestamp(2n);
+ const time3 = new RealTimestamp(3n);
+ const time4 = new RealTimestamp(4n);
+ const time5 = new RealTimestamp(5n);
+ const time6 = new RealTimestamp(6n);
+ const time7 = new RealTimestamp(7n);
+ const time8 = new RealTimestamp(8n);
+ const time9 = new RealTimestamp(9n);
+ const time10 = new RealTimestamp(10n);
+ const time10seconds = new RealTimestamp(10n * 1000000000n);
+
+ describe('ProtoLog <-> WindowManager', () => {
+ let protoLog: Trace<LogMessage>;
+ let windowManager: Trace<WindowManagerState>;
+ let traces: Traces;
+
+ beforeAll(() => {
+ // Frames F0 F1
+ // |<------>| |<->|
+ // PROTO_LOG: 0 1 2 3 4 5
+ // WINDOW_MANAGER: 0 1
+ // Time: 0 1 2 3 4 5 6
+ protoLog = new TraceBuilder<LogMessage>()
+ .setEntries([
+ 'entry-0' as unknown as LogMessage,
+ 'entry-1' as unknown as LogMessage,
+ 'entry-2' as unknown as LogMessage,
+ 'entry-3' as unknown as LogMessage,
+ 'entry-4' as unknown as LogMessage,
+ 'entry-5' as unknown as LogMessage,
+ ])
+ .setTimestamps([time0, time1, time2, time4, time5, time6])
+ .build();
+
+ windowManager = new TraceBuilder<WindowManagerState>()
+ .setEntries([
+ 'entry-0' as unknown as WindowManagerState,
+ 'entry-1' as unknown as WindowManagerState,
+ ])
+ .setTimestamps([time3, time5])
+ .build();
+
+ traces = new Traces();
+ traces.setTrace(TraceType.PROTO_LOG, protoLog);
+ traces.setTrace(TraceType.WINDOW_MANAGER, windowManager);
+ new FrameMapper(traces).computeMapping();
+ });
+
+ it('associates entries/frames', () => {
+ const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+ expectedFrames.set(
+ 0,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.PROTO_LOG, ['entry-0', 'entry-1', 'entry-2']],
+ [TraceType.WINDOW_MANAGER, ['entry-0']],
+ ])
+ );
+ expectedFrames.set(
+ 1,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.PROTO_LOG, ['entry-3', 'entry-4']],
+ [TraceType.WINDOW_MANAGER, ['entry-1']],
+ ])
+ );
+
+ expect(TracesUtils.extractFrames(traces)).toEqual(expectedFrames);
+ });
+ });
+
+ describe('IME <-> WindowManager', () => {
+ let ime: Trace<object>;
+ let windowManager: Trace<WindowManagerState>;
+ let traces: Traces;
+
+ beforeAll(() => {
+ // IME: 0--1--2 3
+ // | |
+ // WINDOW_MANAGER: 0 1 2
+ // Time: 0 1 2 3 4 5
+ ime = new TraceBuilder<object>()
+ .setEntries([
+ 'entry-0' as unknown as object,
+ 'entry-1' as unknown as object,
+ 'entry-2' as unknown as object,
+ 'entry-3' as unknown as object,
+ ])
+ .setTimestamps([time0, time1, time2, time4])
+ .build();
+
+ windowManager = new TraceBuilder<WindowManagerState>()
+ .setEntries([
+ 'entry-0' as unknown as WindowManagerState,
+ 'entry-1' as unknown as WindowManagerState,
+ 'entry-2' as unknown as WindowManagerState,
+ ])
+ .setTimestamps([time1, time4, time5])
+ .build();
+
+ traces = new Traces();
+ traces.setTrace(TraceType.INPUT_METHOD_CLIENTS, ime);
+ traces.setTrace(TraceType.WINDOW_MANAGER, windowManager);
+ new FrameMapper(traces).computeMapping();
+ });
+
+ it('associates entries/frames', () => {
+ const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+ expectedFrames.set(
+ 0,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.INPUT_METHOD_CLIENTS, ['entry-0', 'entry-1', 'entry-2']],
+ [TraceType.WINDOW_MANAGER, ['entry-0']],
+ ])
+ );
+ expectedFrames.set(
+ 1,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.INPUT_METHOD_CLIENTS, ['entry-3']],
+ [TraceType.WINDOW_MANAGER, ['entry-1']],
+ ])
+ );
+ expectedFrames.set(
+ 2,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.INPUT_METHOD_CLIENTS, []],
+ [TraceType.WINDOW_MANAGER, ['entry-2']],
+ ])
+ );
+
+ expect(TracesUtils.extractFrames(traces)).toEqual(expectedFrames);
+ });
+ });
+
+ describe('WindowManager <-> Transactions', () => {
+ let windowManager: Trace<WindowManagerState>;
+ let transactions: Trace<object>;
+ let traces: Traces;
+
+ beforeAll(() => {
+ // WINDOW_MANAGER: 0 1 2 3
+ // | | | \
+ // TRANSACTIONS: 0 1 2--3 4 5 ... 6 <-- ignored (not connected) because too far
+ // | | | | | |
+ // Frames: 0 1 2 3 4 ... 5
+ // Time: 0 1 2 3 4 5 6 ... 10s
+ windowManager = new TraceBuilder<LogMessage>()
+ .setEntries([
+ 'entry-0' as unknown as WindowManagerState,
+ 'entry-1' as unknown as WindowManagerState,
+ 'entry-2' as unknown as WindowManagerState,
+ 'entry-3' as unknown as WindowManagerState,
+ ])
+ .setTimestamps([time1, time2, time4, time5])
+ .build();
+
+ transactions = new TraceBuilder<object>()
+ .setEntries([
+ 'entry-0' as unknown as object,
+ 'entry-1' as unknown as object,
+ 'entry-2' as unknown as object,
+ 'entry-3' as unknown as object,
+ 'entry-4' as unknown as object,
+ 'entry-5' as unknown as object,
+ 'entry-6' as unknown as object,
+ ])
+ .setTimestamps([time0, time1, time2, time3, time4, time5, time10seconds])
+ .setFrame(0, 0)
+ .setFrame(1, 1)
+ .setFrame(2, 2)
+ .setFrame(3, 2)
+ .setFrame(4, 3)
+ .setFrame(5, 4)
+ .setFrame(6, 5)
+ .build();
+
+ traces = new Traces();
+ traces.setTrace(TraceType.WINDOW_MANAGER, windowManager);
+ traces.setTrace(TraceType.TRANSACTIONS, transactions);
+ new FrameMapper(traces).computeMapping();
+ });
+
+ it('associates entries/frames', () => {
+ const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+ expectedFrames.set(
+ 0,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.WINDOW_MANAGER, []],
+ [TraceType.TRANSACTIONS, ['entry-0']],
+ ])
+ );
+ expectedFrames.set(
+ 1,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.WINDOW_MANAGER, ['entry-0']],
+ [TraceType.TRANSACTIONS, ['entry-1']],
+ ])
+ );
+ expectedFrames.set(
+ 2,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.WINDOW_MANAGER, ['entry-1']],
+ [TraceType.TRANSACTIONS, ['entry-2', 'entry-3']],
+ ])
+ );
+ expectedFrames.set(
+ 3,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.WINDOW_MANAGER, ['entry-2']],
+ [TraceType.TRANSACTIONS, ['entry-4']],
+ ])
+ );
+ expectedFrames.set(
+ 4,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.WINDOW_MANAGER, ['entry-3']],
+ [TraceType.TRANSACTIONS, ['entry-5']],
+ ])
+ );
+ expectedFrames.set(
+ 5,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.WINDOW_MANAGER, []],
+ [TraceType.TRANSACTIONS, ['entry-6']],
+ ])
+ );
+
+ expect(TracesUtils.extractFrames(traces)).toEqual(expectedFrames);
+ });
+ });
+
+ describe('Transactions <-> SurfaceFlinger', () => {
+ let transactions: Trace<object>;
+ let surfaceFlinger: Trace<LayerTraceEntry>;
+ let traces: Traces;
+
+ beforeAll(() => {
+ // TRANSACTIONS: 0 1--2 3 4
+ // \ \ \
+ // \ \ \
+ // SURFACE_FLINGER: 0 1 2
+ transactions = new TraceBuilder<object>()
+ .setEntries([
+ {id: 0, vsyncId: createVsyncId(0)},
+ {id: 1, vsyncId: createVsyncId(10)},
+ {id: 2, vsyncId: createVsyncId(10)},
+ {id: 3, vsyncId: createVsyncId(20)},
+ {id: 4, vsyncId: createVsyncId(30)},
+ ])
+ .setTimestamps([time0, time1, time2, time5, time6])
+ .build();
+
+ surfaceFlinger = new TraceBuilder<LayerTraceEntry>()
+ .setEntries([
+ {id: 0, vSyncId: createVsyncId(0)} as unknown as LayerTraceEntry,
+ {id: 1, vSyncId: createVsyncId(10)} as unknown as LayerTraceEntry,
+ {id: 2, vSyncId: createVsyncId(20)} as unknown as LayerTraceEntry,
+ ])
+ .setTimestamps([time0, time1, time2])
+ .build();
+
+ traces = new Traces();
+ traces.setTrace(TraceType.TRANSACTIONS, transactions);
+ traces.setTrace(TraceType.SURFACE_FLINGER, surfaceFlinger);
+ new FrameMapper(traces).computeMapping();
+ });
+
+ it('associates entries/frames', () => {
+ const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+ expectedFrames.set(
+ 0,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TRANSACTIONS, [transactions.getEntry(0).getValue()]],
+ [TraceType.SURFACE_FLINGER, [surfaceFlinger.getEntry(0).getValue()]],
+ ])
+ );
+ expectedFrames.set(
+ 1,
+ new Map<TraceType, Array<{}>>([
+ [
+ TraceType.TRANSACTIONS,
+ [transactions.getEntry(1).getValue(), transactions.getEntry(2).getValue()],
+ ],
+ [TraceType.SURFACE_FLINGER, [surfaceFlinger.getEntry(1).getValue()]],
+ ])
+ );
+ expectedFrames.set(
+ 2,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TRANSACTIONS, [transactions.getEntry(3).getValue()]],
+ [TraceType.SURFACE_FLINGER, [surfaceFlinger.getEntry(2).getValue()]],
+ ])
+ );
+
+ expect(TracesUtils.extractFrames(traces)).toEqual(expectedFrames);
+ });
+ });
+
+ describe('SurfaceFlinger <-> ScreenRecording', () => {
+ let surfaceFlinger: Trace<LayerTraceEntry>;
+ let screenRecording: Trace<ScreenRecordingTraceEntry>;
+ let traces: Traces;
+
+ beforeAll(() => {
+ // SURFACE_FLINGER: 0 1 2--- 3 4 5 6
+ // \ \ \ \
+ // \ \ \ \
+ // SCREEN_RECORDING: 0 1 2 3 4 ... 5 <-- ignored (not connected) because too far
+ // Time: 0 1 2 3 4 5 6 7 8 10s
+ surfaceFlinger = new TraceBuilder<LayerTraceEntry>()
+ .setEntries([
+ 'entry-0' as unknown as LayerTraceEntry,
+ 'entry-1' as unknown as LayerTraceEntry,
+ 'entry-2' as unknown as LayerTraceEntry,
+ 'entry-3' as unknown as LayerTraceEntry,
+ 'entry-4' as unknown as LayerTraceEntry,
+ 'entry-5' as unknown as LayerTraceEntry,
+ 'entry-6' as unknown as LayerTraceEntry,
+ ])
+ .setTimestamps([time0, time1, time2, time4, time6, time7, time8])
+ .build();
+
+ screenRecording = new TraceBuilder<ScreenRecordingTraceEntry>()
+ .setEntries([
+ 'entry-0' as unknown as ScreenRecordingTraceEntry,
+ 'entry-1' as unknown as ScreenRecordingTraceEntry,
+ 'entry-2' as unknown as ScreenRecordingTraceEntry,
+ 'entry-3' as unknown as ScreenRecordingTraceEntry,
+ 'entry-4' as unknown as ScreenRecordingTraceEntry,
+ 'entry-5' as unknown as ScreenRecordingTraceEntry,
+ ])
+ .setTimestamps([time0, time3, time4, time5, time8, time10seconds])
+ .build();
+
+ traces = new Traces();
+ traces.setTrace(TraceType.SURFACE_FLINGER, surfaceFlinger);
+ traces.setTrace(TraceType.SCREEN_RECORDING, screenRecording);
+ new FrameMapper(traces).computeMapping();
+ });
+
+ it('associates entries/frames', () => {
+ const expectedFrames = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+ expectedFrames.set(
+ 0,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.SURFACE_FLINGER, []],
+ [TraceType.SCREEN_RECORDING, ['entry-0']],
+ ])
+ );
+ expectedFrames.set(
+ 1,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.SURFACE_FLINGER, ['entry-2']],
+ [TraceType.SCREEN_RECORDING, ['entry-1']],
+ ])
+ );
+ expectedFrames.set(
+ 2,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.SURFACE_FLINGER, ['entry-2']],
+ [TraceType.SCREEN_RECORDING, ['entry-2']],
+ ])
+ );
+ expectedFrames.set(
+ 3,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.SURFACE_FLINGER, ['entry-3']],
+ [TraceType.SCREEN_RECORDING, ['entry-3']],
+ ])
+ );
+ expectedFrames.set(
+ 4,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.SURFACE_FLINGER, ['entry-5']],
+ [TraceType.SCREEN_RECORDING, ['entry-4']],
+ ])
+ );
+ expectedFrames.set(
+ 5,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.SURFACE_FLINGER, []],
+ [TraceType.SCREEN_RECORDING, ['entry-5']],
+ ])
+ );
+
+ expect(TracesUtils.extractFrames(traces)).toEqual(expectedFrames);
+ });
+ });
+
+ const createVsyncId = (value: number): object => {
+ return {
+ toString() {
+ return value.toString();
+ },
+ };
+ };
+});
diff --git a/tools/winscope/src/trace/index_types.ts b/tools/winscope/src/trace/index_types.ts
new file mode 100644
index 0000000..b3c628c
--- /dev/null
+++ b/tools/winscope/src/trace/index_types.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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 type RelativeEntryIndex = number;
+export type AbsoluteEntryIndex = number;
+export type AbsoluteFrameIndex = number;
+
+// entries = [start; end[ (end not included)
+export interface EntriesRange {
+ start: AbsoluteEntryIndex;
+ end: AbsoluteEntryIndex;
+}
+
+// frames = [start; end[ (end not included)
+export interface FramesRange {
+ start: AbsoluteFrameIndex;
+ end: AbsoluteFrameIndex;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/trace/loaded_trace.ts
similarity index 73%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/trace/loaded_trace.ts
index bfe19ce..eb99b51 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/trace/loaded_trace.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+import {TraceType} from './trace_type';
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class LoadedTrace {
+ constructor(public descriptors: string[], public type: TraceType) {}
+}
diff --git a/tools/winscope/src/trace/parser.ts b/tools/winscope/src/trace/parser.ts
new file mode 100644
index 0000000..d328618
--- /dev/null
+++ b/tools/winscope/src/trace/parser.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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 {Timestamp, TimestampType} from './timestamp';
+import {TraceType} from './trace_type';
+
+export interface Parser<T> {
+ getTraceType(): TraceType;
+ getLengthEntries(): number;
+ getTimestamps(type: TimestampType): Timestamp[] | undefined;
+ getEntry(index: number, timestampType: TimestampType): T;
+
+ getDescriptors(): string[];
+}
diff --git a/tools/winscope/src/trace/parser_mock.ts b/tools/winscope/src/trace/parser_mock.ts
new file mode 100644
index 0000000..c87d293
--- /dev/null
+++ b/tools/winscope/src/trace/parser_mock.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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 {Parser} from './parser';
+import {RealTimestamp, Timestamp, TimestampType} from './timestamp';
+import {TraceFile} from './trace_file';
+import {TraceType} from './trace_type';
+
+export class ParserMock<T> implements Parser<T> {
+ constructor(private readonly timestamps: RealTimestamp[], private readonly entries: T[]) {
+ if (timestamps.length !== entries.length) {
+ throw new Error(`Timestamps and entries must have the same length`);
+ }
+ }
+
+ getTraceType(): TraceType {
+ return TraceType.SURFACE_FLINGER;
+ }
+
+ getTraceFile(): TraceFile {
+ return new TraceFile(new File([], 'file_name'));
+ }
+
+ getLengthEntries(): number {
+ return this.entries.length;
+ }
+
+ getTimestamps(type: TimestampType): Timestamp[] | undefined {
+ if (type !== TimestampType.REAL) {
+ throw new Error('Parser mock contains only real timestamps');
+ }
+ return this.timestamps;
+ }
+
+ getEntry(index: number): T {
+ return this.entries[index];
+ }
+
+ getDescriptors(): string[] {
+ return ['MockTrace'];
+ }
+}
diff --git a/tools/winscope/src/trace/protolog.ts b/tools/winscope/src/trace/protolog.ts
new file mode 100644
index 0000000..7139a08
--- /dev/null
+++ b/tools/winscope/src/trace/protolog.ts
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2022 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 {TimeUtils} from 'common/time_utils';
+import configJson from '../../../../../frameworks/base/data/etc/services.core.protolog.json';
+import {ElapsedTimestamp, RealTimestamp, TimestampType} from './timestamp';
+
+class LogMessage {
+ text: string;
+ time: string;
+ tag: string;
+ level: string;
+ at: string;
+ timestamp: bigint;
+
+ constructor(
+ text: string,
+ time: string,
+ tag: string,
+ level: string,
+ at: string,
+ timestamp: bigint
+ ) {
+ this.text = text;
+ this.time = time;
+ this.tag = tag;
+ this.level = level;
+ this.at = at;
+ this.timestamp = timestamp;
+ }
+}
+
+class FormattedLogMessage extends LogMessage {
+ constructor(
+ proto: any,
+ timestampType: TimestampType,
+ realToElapsedTimeOffsetNs: bigint | undefined
+ ) {
+ const text =
+ proto.messageHash.toString() +
+ ' - [' +
+ proto.strParams.toString() +
+ '] [' +
+ proto.sint64Params.toString() +
+ '] [' +
+ proto.doubleParams.toString() +
+ '] [' +
+ proto.booleanParams.toString() +
+ ']';
+
+ let time: string;
+ let timestamp: bigint;
+ if (
+ timestampType === TimestampType.REAL &&
+ realToElapsedTimeOffsetNs !== undefined &&
+ realToElapsedTimeOffsetNs !== 0n
+ ) {
+ timestamp = realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.format(new RealTimestamp(timestamp));
+ } else {
+ timestamp = BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.format(new ElapsedTimestamp(timestamp));
+ }
+
+ super(text, time, 'INVALID', 'invalid', '', timestamp);
+ }
+}
+
+class UnformattedLogMessage extends LogMessage {
+ constructor(
+ proto: any,
+ timestampType: TimestampType,
+ realToElapsedTimeOffsetNs: bigint | undefined,
+ message: any
+ ) {
+ let time: string;
+ let timestamp: bigint;
+ if (
+ timestampType === TimestampType.REAL &&
+ realToElapsedTimeOffsetNs !== undefined &&
+ realToElapsedTimeOffsetNs !== 0n
+ ) {
+ timestamp = realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.format(
+ new RealTimestamp(realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos))
+ );
+ } else {
+ timestamp = BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.format(new ElapsedTimestamp(timestamp));
+ }
+
+ super(
+ formatText(message.message, proto),
+ time,
+ (configJson as any).groups[message.group].tag,
+ message.level,
+ message.at,
+ timestamp
+ );
+ }
+}
+
+function formatText(messageFormat: any, data: any) {
+ let out = '';
+
+ const strParams: string[] = data.strParams;
+ let strParamsIdx = 0;
+ const sint64Params: number[] = data.sint64Params;
+ let sint64ParamsIdx = 0;
+ const doubleParams: number[] = data.doubleParams;
+ let doubleParamsIdx = 0;
+ const booleanParams: number[] = 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<T>(arr: T[], idx: number): T {
+ if (arr.length <= idx) {
+ throw new Error('No param for format string conversion');
+ }
+ return arr[idx];
+}
+
+export {FormattedLogMessage, LogMessage, UnformattedLogMessage};
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/trace/screen_recording.ts
similarity index 73%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/trace/screen_recording.ts
index bfe19ce..342ab1a 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/trace/screen_recording.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+class ScreenRecordingTraceEntry {
+ constructor(public videoTimeSeconds: number, public videoData: Blob) {}
+}
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export {ScreenRecordingTraceEntry};
diff --git a/tools/winscope/src/trace/screen_recording_utils.ts b/tools/winscope/src/trace/screen_recording_utils.ts
new file mode 100644
index 0000000..e882f8c
--- /dev/null
+++ b/tools/winscope/src/trace/screen_recording_utils.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 {Timestamp} from './timestamp';
+
+class ScreenRecordingUtils {
+ // Video time correction epsilon. Without correction, we could display the previous frame.
+ // This correction was already present in the legacy Winscope.
+ private static readonly EPSILON_SECONDS = 0.00001;
+
+ static timestampToVideoTimeSeconds(firstTimestamp: Timestamp, currentTimestamp: Timestamp) {
+ if (firstTimestamp.getType() !== currentTimestamp.getType()) {
+ throw new Error('Attempted to use timestamps with different type');
+ }
+ const videoTimeSeconds =
+ Number(currentTimestamp.getValueNs() - firstTimestamp.getValueNs()) / 1000000000 +
+ ScreenRecordingUtils.EPSILON_SECONDS;
+ return videoTimeSeconds;
+ }
+}
+
+export {ScreenRecordingUtils};
diff --git a/tools/winscope/src/trace/timestamp.ts b/tools/winscope/src/trace/timestamp.ts
new file mode 100644
index 0000000..23999d1
--- /dev/null
+++ b/tools/winscope/src/trace/timestamp.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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 enum TimestampType {
+ ELAPSED = 'ELAPSED',
+ REAL = 'REAL',
+}
+
+export class Timestamp {
+ private readonly type: TimestampType;
+ private readonly valueNs: bigint;
+
+ constructor(type: TimestampType, valueNs: bigint) {
+ this.type = type;
+ this.valueNs = valueNs;
+ }
+
+ static from(
+ timestampType: TimestampType,
+ elapsedTimestamp: bigint,
+ realToElapsedTimeOffsetNs: bigint | undefined = undefined
+ ) {
+ switch (timestampType) {
+ case TimestampType.REAL:
+ if (realToElapsedTimeOffsetNs === undefined) {
+ throw new Error("realToElapsedTimeOffsetNs can't be undefined to use real timestamp");
+ }
+ return new Timestamp(TimestampType.REAL, elapsedTimestamp + realToElapsedTimeOffsetNs);
+ case TimestampType.ELAPSED:
+ return new Timestamp(TimestampType.ELAPSED, elapsedTimestamp);
+ default:
+ throw new Error('Unhandled timestamp type');
+ }
+ }
+
+ getType(): TimestampType {
+ return this.type;
+ }
+
+ getValueNs(): bigint {
+ return this.valueNs;
+ }
+
+ valueOf(): bigint {
+ return this.getValueNs();
+ }
+
+ add(nanoseconds: bigint): Timestamp {
+ return new Timestamp(this.type, this.valueNs + nanoseconds);
+ }
+}
+
+export class RealTimestamp extends Timestamp {
+ constructor(valueNs: bigint) {
+ super(TimestampType.REAL, valueNs);
+ }
+}
+
+export class ElapsedTimestamp extends Timestamp {
+ constructor(valueNs: bigint) {
+ super(TimestampType.ELAPSED, valueNs);
+ }
+}
diff --git a/tools/winscope/src/trace/timestamp_test.ts b/tools/winscope/src/trace/timestamp_test.ts
new file mode 100644
index 0000000..91a8b1a
--- /dev/null
+++ b/tools/winscope/src/trace/timestamp_test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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 {Timestamp, TimestampType} from './timestamp';
+
+describe('Timestamp', () => {
+ describe('from', () => {
+ it('throws when missing elapsed timestamp', () => {
+ expect(() => {
+ Timestamp.from(TimestampType.REAL, 100n);
+ }).toThrow();
+ });
+
+ it('can create real timestamp', () => {
+ const timestamp = Timestamp.from(TimestampType.REAL, 100n, 500n);
+ expect(timestamp.getType()).toBe(TimestampType.REAL);
+ expect(timestamp.getValueNs()).toBe(600n);
+ });
+
+ it('can create elapsed timestamp', () => {
+ let timestamp = Timestamp.from(TimestampType.ELAPSED, 100n, 500n);
+ expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
+ expect(timestamp.getValueNs()).toBe(100n);
+
+ timestamp = Timestamp.from(TimestampType.ELAPSED, 100n);
+ expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
+ expect(timestamp.getValueNs()).toBe(100n);
+ });
+ });
+});
diff --git a/tools/winscope/src/trace/trace.ts b/tools/winscope/src/trace/trace.ts
new file mode 100644
index 0000000..dc1ad66
--- /dev/null
+++ b/tools/winscope/src/trace/trace.ts
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2023 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 {ArrayUtils} from 'common/array_utils';
+import {FrameMap} from './frame_map';
+import {
+ AbsoluteEntryIndex,
+ AbsoluteFrameIndex,
+ EntriesRange,
+ FramesRange,
+ RelativeEntryIndex,
+} from './index_types';
+import {Parser} from './parser';
+import {Timestamp, TimestampType} from './timestamp';
+import {TraceFile} from './trace_file';
+import {TraceType} from './trace_type';
+
+export {
+ AbsoluteEntryIndex,
+ AbsoluteFrameIndex,
+ EntriesRange,
+ FramesRange,
+ RelativeEntryIndex,
+} from './index_types';
+
+export class TraceEntry<T> {
+ constructor(
+ private readonly fullTrace: Trace<T>,
+ private readonly parser: Parser<T>,
+ private readonly index: AbsoluteEntryIndex,
+ private readonly timestamp: Timestamp,
+ private readonly framesRange: FramesRange | undefined
+ ) {}
+
+ getFullTrace(): Trace<T> {
+ return this.fullTrace;
+ }
+
+ getIndex(): AbsoluteEntryIndex {
+ return this.index;
+ }
+
+ getTimestamp(): Timestamp {
+ return this.timestamp;
+ }
+
+ getFramesRange(): FramesRange | undefined {
+ if (!this.fullTrace.hasFrameInfo()) {
+ throw new Error(
+ `Trace ${this.fullTrace.type} can't be accessed in frame domain (no frame info available)`
+ );
+ }
+ return this.framesRange;
+ }
+
+ getValue(): T {
+ return this.parser.getEntry(this.index, this.timestamp.getType());
+ }
+}
+
+export class Trace<T> {
+ readonly file?: TraceFile;
+ readonly lengthEntries: number;
+ readonly fullTrace: Trace<T>;
+
+ private timestampType: TimestampType | undefined;
+ private readonly entriesRange: EntriesRange;
+ private frameMap?: FrameMap;
+ private framesRange?: FramesRange;
+
+ static newUninitializedTrace<T>(parser: Parser<T>): Trace<T> {
+ return new Trace(
+ parser.getTraceType(),
+ parser,
+ parser.getDescriptors(),
+ undefined,
+ undefined,
+ undefined
+ );
+ }
+
+ static newInitializedTrace<T>(
+ type: TraceType,
+ entryProvider: Parser<T>,
+ descriptors: string[],
+ timestampType: TimestampType,
+ entriesRange: EntriesRange
+ ): Trace<T> {
+ return new Trace(type, entryProvider, descriptors, undefined, timestampType, entriesRange);
+ }
+
+ init(timestampType: TimestampType) {
+ this.timestampType = timestampType;
+ }
+
+ private constructor(
+ readonly type: TraceType,
+ readonly parser: Parser<T>,
+ readonly descriptors: string[],
+ fullTrace: Trace<T> | undefined,
+ timestampType: TimestampType | undefined,
+ entriesRange: EntriesRange | undefined
+ ) {
+ this.fullTrace = fullTrace ?? this;
+ this.entriesRange = entriesRange ?? {start: 0, end: parser.getLengthEntries()};
+ this.lengthEntries = this.entriesRange.end - this.entriesRange.start;
+ this.timestampType = timestampType;
+ }
+
+ getDescriptors(): string[] {
+ return this.parser.getDescriptors();
+ }
+
+ getTimestampType(): TimestampType {
+ if (this.timestampType === undefined) {
+ throw new Error('Trace no fully initialized yet!');
+ }
+ return this.timestampType;
+ }
+
+ setFrameInfo(frameMap: FrameMap, framesRange: FramesRange | undefined) {
+ if (frameMap.lengthEntries !== this.fullTrace.lengthEntries) {
+ throw new Error('Attemped to set a frame map with incompatible number of entries');
+ }
+ this.frameMap = frameMap;
+ this.framesRange = framesRange;
+ }
+
+ hasFrameInfo(): boolean {
+ return this.frameMap !== undefined;
+ }
+
+ getEntry(index: RelativeEntryIndex): TraceEntry<T> {
+ const entry = this.convertToAbsoluteEntryIndex(index) as AbsoluteEntryIndex;
+ if (entry < this.entriesRange.start || entry >= this.entriesRange.end) {
+ throw new Error(
+ `Trace entry's index out of bounds. Input relative index: ${index}. Slice length: ${this.lengthEntries}.`
+ );
+ }
+ const frames = this.clampFramesRangeToSliceBounds(
+ this.frameMap?.getFramesRange({start: entry, end: entry + 1})
+ );
+ return new TraceEntry<T>(
+ this.fullTrace,
+ this.parser,
+ entry,
+ this.getFullTraceTimestamps()[entry],
+ frames
+ );
+ }
+
+ getFrame(frame: AbsoluteFrameIndex): Trace<T> {
+ this.checkTraceCanBeAccessedInFrameDomain();
+ const entries = this.frameMap!.getEntriesRange({start: frame, end: frame + 1});
+ return this.createSlice(entries, {start: frame, end: frame + 1});
+ }
+
+ findClosestEntry(time: Timestamp): TraceEntry<T> | undefined {
+ this.checkTimestampIsCompatible(time);
+ if (this.lengthEntries === 0) {
+ return undefined;
+ }
+
+ const entry = this.clampEntryToSliceBounds(
+ ArrayUtils.binarySearchFirstGreaterOrEqual(this.getFullTraceTimestamps(), time)
+ );
+ if (entry === undefined || entry === this.entriesRange.end) {
+ return this.getEntry(this.lengthEntries - 1);
+ }
+
+ if (entry === this.entriesRange.start) {
+ return this.getEntry(0);
+ }
+
+ const abs = (time: bigint) => (time < 0 ? -time : time);
+ const timeDiff = abs(this.getFullTraceTimestamps()[entry].getValueNs() - time.getValueNs());
+ const prevEntry = entry - 1;
+ const prevTimeDiff = abs(
+ this.getFullTraceTimestamps()[prevEntry].getValueNs() - time.getValueNs()
+ );
+ if (prevTimeDiff < timeDiff) {
+ return this.getEntry(prevEntry - this.entriesRange.start);
+ }
+ return this.getEntry(entry - this.entriesRange.start);
+ }
+
+ findFirstGreaterOrEqualEntry(time: Timestamp): TraceEntry<T> | undefined {
+ this.checkTimestampIsCompatible(time);
+ if (this.lengthEntries === 0) {
+ return undefined;
+ }
+
+ const pos = this.clampEntryToSliceBounds(
+ ArrayUtils.binarySearchFirstGreaterOrEqual(this.getFullTraceTimestamps(), time)
+ );
+ if (pos === undefined || pos === this.entriesRange.end) {
+ return undefined;
+ }
+
+ const entry = this.getEntry(pos - this.entriesRange.start);
+ if (entry.getTimestamp() < time) {
+ return undefined;
+ }
+
+ return entry;
+ }
+
+ findFirstGreaterEntry(time: Timestamp): TraceEntry<T> | undefined {
+ this.checkTimestampIsCompatible(time);
+ if (this.lengthEntries === 0) {
+ return undefined;
+ }
+
+ const pos = this.clampEntryToSliceBounds(
+ ArrayUtils.binarySearchFirstGreater(this.getFullTraceTimestamps(), time)
+ );
+ if (pos === undefined || pos === this.entriesRange.end) {
+ return undefined;
+ }
+
+ const entry = this.getEntry(pos - this.entriesRange.start);
+ if (entry.getTimestamp() <= time) {
+ return undefined;
+ }
+
+ return entry;
+ }
+
+ findLastLowerOrEqualEntry(timestamp: Timestamp): TraceEntry<T> | undefined {
+ if (this.lengthEntries === 0) {
+ return undefined;
+ }
+ const firstGreater = this.findFirstGreaterEntry(timestamp);
+ if (!firstGreater) {
+ return this.getEntry(this.lengthEntries - 1);
+ }
+ if (firstGreater.getIndex() === this.entriesRange.start) {
+ return undefined;
+ }
+ return this.getEntry(firstGreater.getIndex() - this.entriesRange.start - 1);
+ }
+
+ findLastLowerEntry(timestamp: Timestamp): TraceEntry<T> | undefined {
+ if (this.lengthEntries === 0) {
+ return undefined;
+ }
+ const firstGreaterOrEqual = this.findFirstGreaterOrEqualEntry(timestamp);
+ if (!firstGreaterOrEqual) {
+ return this.getEntry(this.lengthEntries - 1);
+ }
+ if (firstGreaterOrEqual.getIndex() === this.entriesRange.start) {
+ return undefined;
+ }
+ return this.getEntry(firstGreaterOrEqual.getIndex() - this.entriesRange.start - 1);
+ }
+
+ sliceEntries(start?: RelativeEntryIndex, end?: RelativeEntryIndex): Trace<T> {
+ const startEntry =
+ this.clampEntryToSliceBounds(this.convertToAbsoluteEntryIndex(start)) ??
+ this.entriesRange.start;
+ const endEntry =
+ this.clampEntryToSliceBounds(this.convertToAbsoluteEntryIndex(end)) ?? this.entriesRange.end;
+ const entries: EntriesRange = {
+ start: startEntry,
+ end: endEntry,
+ };
+ const frames = this.frameMap?.getFramesRange(entries);
+ return this.createSlice(entries, frames);
+ }
+
+ sliceTime(start?: Timestamp, end?: Timestamp): Trace<T> {
+ this.checkTimestampIsCompatible(start);
+ this.checkTimestampIsCompatible(end);
+ const startEntry =
+ start === undefined
+ ? this.entriesRange.start
+ : this.clampEntryToSliceBounds(
+ ArrayUtils.binarySearchFirstGreaterOrEqual(this.getFullTraceTimestamps(), start)
+ ) ?? this.entriesRange.end;
+ const endEntry =
+ end === undefined
+ ? this.entriesRange.end
+ : this.clampEntryToSliceBounds(
+ ArrayUtils.binarySearchFirstGreaterOrEqual(this.getFullTraceTimestamps(), end)
+ ) ?? this.entriesRange.end;
+ const entries: EntriesRange = {
+ start: startEntry,
+ end: endEntry,
+ };
+ const frames = this.frameMap?.getFramesRange(entries);
+ return this.createSlice(entries, frames);
+ }
+
+ sliceFrames(start?: AbsoluteFrameIndex, end?: AbsoluteFrameIndex): Trace<T> {
+ this.checkTraceCanBeAccessedInFrameDomain();
+ if (!this.framesRange) {
+ return this.createSlice(undefined, undefined);
+ }
+ const frames: FramesRange = {
+ start: this.clampFrameToSliceBounds(start) ?? this.framesRange.start,
+ end: this.clampFrameToSliceBounds(end) ?? this.framesRange.end,
+ };
+ const entries = this.frameMap!.getEntriesRange(frames);
+ return this.createSlice(entries, frames);
+ }
+
+ forEachEntry(callback: (pos: TraceEntry<T>, index: RelativeEntryIndex) => void) {
+ for (let index = 0; index < this.lengthEntries; ++index) {
+ callback(this.getEntry(index), index);
+ }
+ }
+
+ forEachTimestamp(callback: (timestamp: Timestamp, index: RelativeEntryIndex) => void) {
+ const timestamps = this.getFullTraceTimestamps();
+ for (let index = 0; index < this.lengthEntries; ++index) {
+ callback(timestamps[this.entriesRange.start + index], index);
+ }
+ }
+
+ forEachFrame(callback: (frame: Trace<T>, index: AbsoluteFrameIndex) => void) {
+ this.checkTraceCanBeAccessedInFrameDomain();
+ if (!this.framesRange) {
+ return;
+ }
+ for (let frame = this.framesRange.start; frame < this.framesRange.end; ++frame) {
+ callback(this.getFrame(frame), frame);
+ }
+ }
+
+ getFramesRange(): FramesRange | undefined {
+ this.checkTraceCanBeAccessedInFrameDomain();
+ return this.framesRange;
+ }
+
+ private getFullTraceTimestamps(): Timestamp[] {
+ if (this.timestampType === undefined) {
+ throw new Error('Forgot to initialize trace?');
+ }
+
+ const timestamps = this.parser.getTimestamps(this.timestampType);
+ if (!timestamps) {
+ throw new Error(`Timestamp type ${this.timestampType} is expected to be available`);
+ }
+ return timestamps;
+ }
+
+ private convertToAbsoluteEntryIndex(
+ index: RelativeEntryIndex | undefined
+ ): AbsoluteEntryIndex | undefined {
+ if (index === undefined) {
+ return undefined;
+ }
+ if (index < 0) {
+ return this.entriesRange.end + index;
+ }
+ return this.entriesRange.start + index;
+ }
+
+ private createSlice(
+ entries: EntriesRange | undefined,
+ frames: FramesRange | undefined
+ ): Trace<T> {
+ entries = this.clampEntriesRangeToSliceBounds(entries);
+ frames = this.clampFramesRangeToSliceBounds(frames);
+
+ if (entries === undefined || entries.start >= entries.end) {
+ entries = {
+ start: this.entriesRange.end,
+ end: this.entriesRange.end,
+ };
+ }
+
+ const slice = new Trace<T>(
+ this.type,
+ this.parser,
+ this.descriptors,
+ this.fullTrace,
+ this.timestampType,
+ entries
+ );
+
+ if (this.frameMap) {
+ slice.setFrameInfo(this.frameMap, frames);
+ }
+
+ return slice;
+ }
+
+ private clampEntriesRangeToSliceBounds(
+ entries: EntriesRange | undefined
+ ): EntriesRange | undefined {
+ if (entries === undefined) {
+ return undefined;
+ }
+ return {
+ start: this.clampEntryToSliceBounds(entries.start) as AbsoluteEntryIndex,
+ end: this.clampEntryToSliceBounds(entries.end) as AbsoluteEntryIndex,
+ };
+ }
+
+ private clampFramesRangeToSliceBounds(frames: FramesRange | undefined): FramesRange | undefined {
+ if (frames === undefined) {
+ return undefined;
+ }
+ return {
+ start: this.clampFrameToSliceBounds(frames.start) as AbsoluteFrameIndex,
+ end: this.clampFrameToSliceBounds(frames.end) as AbsoluteFrameIndex,
+ };
+ }
+
+ private clampEntryToSliceBounds(
+ entry: AbsoluteEntryIndex | undefined
+ ): AbsoluteEntryIndex | undefined {
+ if (entry === undefined) {
+ return undefined;
+ }
+ return Math.min(Math.max(entry, this.entriesRange.start), this.entriesRange.end);
+ }
+
+ private clampFrameToSliceBounds(
+ frame: AbsoluteFrameIndex | undefined
+ ): AbsoluteFrameIndex | undefined {
+ if (!this.framesRange || frame === undefined) {
+ return undefined;
+ }
+
+ if (frame < 0) {
+ throw new Error(`Absolute frame index cannot be negative. Found '${frame}'`);
+ }
+
+ return Math.min(Math.max(frame, this.framesRange.start), this.framesRange.end);
+ }
+
+ private checkTraceCanBeAccessedInFrameDomain() {
+ if (!this.frameMap) {
+ throw new Error(
+ `Trace ${this.type} can't be accessed in frame domain (no frame mapping available)`
+ );
+ }
+ }
+
+ private checkTimestampIsCompatible(timestamp?: Timestamp) {
+ if (!timestamp) {
+ return;
+ }
+ const timestamps = this.parser.getTimestamps(timestamp.getType());
+ if (timestamps === undefined) {
+ throw new Error(
+ `Trace ${this.type} can't be accessed using timestamp of type ${timestamp.getType()}`
+ );
+ }
+ }
+}
diff --git a/tools/winscope/src/trace/trace_entry_finder.ts b/tools/winscope/src/trace/trace_entry_finder.ts
new file mode 100644
index 0000000..33c58d5
--- /dev/null
+++ b/tools/winscope/src/trace/trace_entry_finder.ts
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 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, TraceEntry} from './trace';
+import {TracePosition} from './trace_position';
+import {TraceType} from './trace_type';
+
+export class TraceEntryFinder {
+ static readonly UI_PIPELINE_ORDER = [
+ TraceType.INPUT_METHOD_CLIENTS,
+ TraceType.INPUT_METHOD_SERVICE,
+ TraceType.INPUT_METHOD_MANAGER_SERVICE,
+ TraceType.PROTO_LOG,
+ TraceType.WINDOW_MANAGER,
+ TraceType.TRANSACTIONS,
+ TraceType.SURFACE_FLINGER,
+ TraceType.SCREEN_RECORDING,
+ ];
+
+ static findCorrespondingEntry<T>(
+ trace: Trace<T>,
+ position: TracePosition
+ ): TraceEntry<T> | undefined {
+ if (position.entry?.getFullTrace().type === trace.type) {
+ return position.entry as TraceEntry<T>;
+ }
+
+ if (position.frame !== undefined && trace.hasFrameInfo()) {
+ const frame = trace.getFrame(position.frame);
+ if (frame.lengthEntries > 0) {
+ return frame.getEntry(0);
+ }
+ }
+
+ if (position.entry) {
+ const entryTraceType = position.entry.getFullTrace().type;
+
+ const indexPosition = TraceEntryFinder.UI_PIPELINE_ORDER.findIndex((type) => {
+ return type === entryTraceType;
+ });
+ const indexTrace = TraceEntryFinder.UI_PIPELINE_ORDER.findIndex((type) => {
+ return type === trace.type;
+ });
+
+ if (indexPosition !== undefined && indexTrace !== undefined) {
+ if (indexPosition < indexTrace) {
+ return trace.findFirstGreaterEntry(position.entry.getTimestamp());
+ } else {
+ return trace.findLastLowerEntry(position.entry.getTimestamp());
+ }
+ }
+ }
+
+ return trace.findLastLowerOrEqualEntry(position.timestamp);
+ }
+}
diff --git a/tools/winscope/src/trace/trace_entry_test.ts b/tools/winscope/src/trace/trace_entry_test.ts
new file mode 100644
index 0000000..db9ee8e
--- /dev/null
+++ b/tools/winscope/src/trace/trace_entry_test.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 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 {TraceBuilder} from 'test/unit/trace_builder';
+import {RealTimestamp} from './timestamp';
+import {Trace} from './trace';
+
+describe('TraceEntry', () => {
+ let trace: Trace<string>;
+
+ beforeAll(() => {
+ trace = new TraceBuilder<string>()
+ .setTimestamps([
+ new RealTimestamp(10n),
+ new RealTimestamp(11n),
+ new RealTimestamp(12n),
+ new RealTimestamp(13n),
+ new RealTimestamp(14n),
+ new RealTimestamp(15n),
+ ])
+ .setEntries(['entry-0', 'entry-1', 'entry-2', 'entry-3', 'entry-4', 'entry-5'])
+ .setFrame(0, 0)
+ .setFrame(0, 1)
+ .setFrame(1, 1)
+ .setFrame(2, 1)
+ .setFrame(3, 2)
+ .setFrame(5, 4)
+ .build();
+ });
+
+ it('getFullTrace()', () => {
+ expect(trace.getEntry(0).getFullTrace()).toEqual(trace);
+ expect(trace.sliceEntries(0, 1).getEntry(0).getFullTrace()).toEqual(trace);
+ });
+
+ it('getIndex()', () => {
+ expect(trace.getEntry(0).getIndex()).toEqual(0);
+ expect(trace.sliceEntries(2, 4).getEntry(0).getIndex()).toEqual(2);
+ expect(trace.sliceEntries(2, 4).getEntry(1).getIndex()).toEqual(3);
+ });
+
+ it('getTimestamp()', () => {
+ expect(trace.getEntry(0).getTimestamp()).toEqual(new RealTimestamp(10n));
+ expect(trace.getEntry(1).getTimestamp()).toEqual(new RealTimestamp(11n));
+ });
+
+ it('getFramesRange()', () => {
+ expect(trace.getEntry(0).getFramesRange()).toEqual({start: 0, end: 2});
+ expect(trace.getEntry(1).getFramesRange()).toEqual({start: 1, end: 2});
+ expect(trace.getEntry(2).getFramesRange()).toEqual({start: 1, end: 2});
+ expect(trace.getEntry(3).getFramesRange()).toEqual({start: 2, end: 3});
+ expect(trace.getEntry(4).getFramesRange()).toEqual(undefined);
+ expect(trace.getEntry(5).getFramesRange()).toEqual({start: 4, end: 5});
+ });
+
+ it('getValue()', () => {
+ expect(trace.getEntry(0).getValue()).toEqual('entry-0');
+ expect(trace.getEntry(1).getValue()).toEqual('entry-1');
+ });
+});
diff --git a/tools/winscope/src/trace/trace_file.ts b/tools/winscope/src/trace/trace_file.ts
new file mode 100644
index 0000000..0ae7a98
--- /dev/null
+++ b/tools/winscope/src/trace/trace_file.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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 {FileUtils} from 'common/file_utils';
+
+export class TraceFile {
+ constructor(public file: File, public parentArchive?: File) {}
+
+ getDescriptor(): string {
+ let descriptor = FileUtils.removeDirFromFileName(this.file.name);
+ if (this.parentArchive?.name !== undefined) {
+ descriptor += ` (${this.parentArchive?.name})`;
+ }
+ return descriptor;
+ }
+}
diff --git a/tools/winscope/src/trace/trace_position.ts b/tools/winscope/src/trace/trace_position.ts
new file mode 100644
index 0000000..f0cd118
--- /dev/null
+++ b/tools/winscope/src/trace/trace_position.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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 {AbsoluteFrameIndex} from './index_types';
+import {Timestamp} from './timestamp';
+import {TraceEntry} from './trace';
+
+export class TracePosition {
+ static fromTimestamp(timestamp: Timestamp): TracePosition {
+ return new TracePosition(timestamp);
+ }
+
+ static fromTraceEntry(entry: TraceEntry<{}>): TracePosition {
+ let frame: AbsoluteFrameIndex | undefined;
+ if (entry.getFullTrace().hasFrameInfo()) {
+ const frames = entry.getFramesRange();
+ frame = frames && frames.start < frames.end ? frames.start : undefined;
+ }
+ return new TracePosition(entry.getTimestamp(), frame, entry);
+ }
+
+ isEqual(other: TracePosition): boolean {
+ return (
+ this.timestamp.getValueNs() === other.timestamp.getValueNs() &&
+ this.frame === other.frame &&
+ this.entry?.getFullTrace().type === other.entry?.getFullTrace().type &&
+ this.entry?.getIndex() === other.entry?.getIndex()
+ );
+ }
+
+ private constructor(
+ readonly timestamp: Timestamp,
+ readonly frame?: AbsoluteFrameIndex,
+ readonly entry?: TraceEntry<{}>
+ ) {}
+}
diff --git a/tools/winscope/src/trace/trace_test.ts b/tools/winscope/src/trace/trace_test.ts
new file mode 100644
index 0000000..42da5f4
--- /dev/null
+++ b/tools/winscope/src/trace/trace_test.ts
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (C) 2023 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 {TraceBuilder} from 'test/unit/trace_builder';
+import {TraceUtils} from 'test/unit/trace_utils';
+import {FrameMapBuilder} from './frame_map_builder';
+import {AbsoluteFrameIndex} from './index_types';
+import {RealTimestamp} from './timestamp';
+import {Trace} from './trace';
+
+describe('Trace', () => {
+ let trace: Trace<string>;
+
+ const time9 = new RealTimestamp(9n);
+ const time10 = new RealTimestamp(10n);
+ const time11 = new RealTimestamp(11n);
+ const time12 = new RealTimestamp(12n);
+ const time13 = new RealTimestamp(13n);
+ const time14 = new RealTimestamp(14n);
+ const time15 = new RealTimestamp(15n);
+
+ beforeAll(() => {
+ // Time: 10 11 12 13
+ // Entry: 0 1-2 3 4
+ // | | | |
+ // Frame: 0 1 2 3 4-5 6
+ trace = new TraceBuilder<string>()
+ .setEntries(['entry-0', 'entry-1', 'entry-2', 'entry-3', 'entry-4'])
+ .setTimestamps([time10, time11, time11, time12, time13])
+ .setFrame(0, 0)
+ .setFrame(1, 1)
+ .setFrame(2, 1)
+ .setFrame(3, 4)
+ .setFrame(3, 5)
+ .setFrame(4, 6)
+ .build();
+ });
+
+ it('getEntry()', () => {
+ expect(trace.getEntry(0).getValue()).toEqual('entry-0');
+ expect(trace.getEntry(4).getValue()).toEqual('entry-4');
+ expect(() => {
+ trace.getEntry(5);
+ }).toThrow();
+
+ expect(trace.getEntry(-1).getValue()).toEqual('entry-4');
+ expect(trace.getEntry(-5).getValue()).toEqual('entry-0');
+ expect(() => {
+ trace.getEntry(-6);
+ }).toThrow();
+ });
+
+ it('getFrame()', () => {
+ expect(TraceUtils.extractFrames(trace.getFrame(0))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[0, ['entry-0']]])
+ );
+ expect(TraceUtils.extractFrames(trace.getFrame(1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1', 'entry-2']]])
+ );
+ expect(TraceUtils.extractFrames(trace.getFrame(2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[2, []]])
+ );
+ expect(TraceUtils.extractFrames(trace.getFrame(3))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[3, []]])
+ );
+ expect(TraceUtils.extractFrames(trace.getFrame(4))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[4, ['entry-3']]])
+ );
+ expect(TraceUtils.extractFrames(trace.getFrame(5))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[5, ['entry-3']]])
+ );
+ expect(TraceUtils.extractFrames(trace.getFrame(6))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[6, ['entry-4']]])
+ );
+ });
+
+ it('findClosestEntry()', () => {
+ // empty
+ expect(trace.sliceEntries(0, 0).findClosestEntry(time10)).toBeUndefined();
+
+ // slice
+ const slice = trace.sliceEntries(1, -1);
+ expect(slice.findClosestEntry(time9)?.getValue()).toEqual('entry-1');
+ expect(slice.findClosestEntry(time10)?.getValue()).toEqual('entry-1');
+ expect(slice.findClosestEntry(time11)?.getValue()).toEqual('entry-1');
+ expect(slice.findClosestEntry(time12)?.getValue()).toEqual('entry-3');
+ expect(slice.findClosestEntry(time13)?.getValue()).toEqual('entry-3');
+ expect(slice.findClosestEntry(time14)?.getValue()).toEqual('entry-3');
+
+ // full trace
+ expect(trace.findClosestEntry(time9)?.getValue()).toEqual('entry-0');
+ expect(trace.findClosestEntry(time10)?.getValue()).toEqual('entry-0');
+ expect(trace.findClosestEntry(time11)?.getValue()).toEqual('entry-1');
+ expect(trace.findClosestEntry(time12)?.getValue()).toEqual('entry-3');
+ expect(trace.findClosestEntry(time13)?.getValue()).toEqual('entry-4');
+ expect(trace.findClosestEntry(time14)?.getValue()).toEqual('entry-4');
+ });
+
+ it('findFirstGreaterOrEqualEntry()', () => {
+ // empty
+ expect(trace.sliceEntries(0, 0).findFirstGreaterOrEqualEntry(time10)).toBeUndefined();
+
+ // slice
+ const slice = trace.sliceEntries(1, -1);
+ expect(slice.findFirstGreaterOrEqualEntry(time9)?.getValue()).toEqual('entry-1');
+ expect(slice.findFirstGreaterOrEqualEntry(time10)?.getValue()).toEqual('entry-1');
+ expect(slice.findFirstGreaterOrEqualEntry(time11)?.getValue()).toEqual('entry-1');
+ expect(slice.findFirstGreaterOrEqualEntry(time12)?.getValue()).toEqual('entry-3');
+ expect(slice.findFirstGreaterOrEqualEntry(time13)).toBeUndefined();
+
+ // full trace
+ expect(trace.findFirstGreaterOrEqualEntry(time9)?.getValue()).toEqual('entry-0');
+ expect(trace.findFirstGreaterOrEqualEntry(time10)?.getValue()).toEqual('entry-0');
+ expect(trace.findFirstGreaterOrEqualEntry(time11)?.getValue()).toEqual('entry-1');
+ expect(trace.findFirstGreaterOrEqualEntry(time12)?.getValue()).toEqual('entry-3');
+ expect(trace.findFirstGreaterOrEqualEntry(time13)?.getValue()).toEqual('entry-4');
+ expect(trace.findFirstGreaterOrEqualEntry(time14)).toBeUndefined();
+ });
+
+ it('findFirstGreaterEntry()', () => {
+ // empty
+ expect(trace.sliceEntries(0, 0).findFirstGreaterEntry(time10)).toBeUndefined();
+
+ // slice
+ const slice = trace.sliceEntries(1, -1);
+ expect(slice.findFirstGreaterEntry(time9)?.getValue()).toEqual('entry-1');
+ expect(slice.findFirstGreaterEntry(time10)?.getValue()).toEqual('entry-1');
+ expect(slice.findFirstGreaterEntry(time11)?.getValue()).toEqual('entry-3');
+ expect(slice.findFirstGreaterEntry(time12)).toBeUndefined();
+
+ // full trace
+ expect(trace.findFirstGreaterEntry(time9)?.getValue()).toEqual('entry-0');
+ expect(trace.findFirstGreaterEntry(time10)?.getValue()).toEqual('entry-1');
+ expect(trace.findFirstGreaterEntry(time11)?.getValue()).toEqual('entry-3');
+ expect(trace.findFirstGreaterEntry(time12)?.getValue()).toEqual('entry-4');
+ expect(trace.findFirstGreaterEntry(time13)).toBeUndefined();
+ });
+
+ it('findLastLowerOrEqualEntry()', () => {
+ // empty
+ expect(trace.sliceEntries(0, 0).findLastLowerOrEqualEntry(time10)).toBeUndefined();
+
+ // slice
+ const slice = trace.sliceEntries(1, -1);
+ expect(slice.findLastLowerOrEqualEntry(time9)).toBeUndefined();
+ expect(slice.findLastLowerOrEqualEntry(time10)).toBeUndefined();
+ expect(slice.findLastLowerOrEqualEntry(time11)?.getValue()).toEqual('entry-2');
+ expect(slice.findLastLowerOrEqualEntry(time12)?.getValue()).toEqual('entry-3');
+ expect(slice.findLastLowerOrEqualEntry(time13)?.getValue()).toEqual('entry-3');
+
+ // full trace
+ expect(trace.findLastLowerOrEqualEntry(time9)).toBeUndefined();
+ expect(trace.findLastLowerOrEqualEntry(time10)?.getValue()).toEqual('entry-0');
+ expect(trace.findLastLowerOrEqualEntry(time11)?.getValue()).toEqual('entry-2');
+ expect(trace.findLastLowerOrEqualEntry(time12)?.getValue()).toEqual('entry-3');
+ expect(trace.findLastLowerOrEqualEntry(time13)?.getValue()).toEqual('entry-4');
+ expect(trace.findLastLowerOrEqualEntry(time14)?.getValue()).toEqual('entry-4');
+ });
+
+ it('findLastLowerEntry()', () => {
+ // empty
+ expect(trace.sliceEntries(0, 0).findLastLowerEntry(time10)).toBeUndefined();
+
+ // slice
+ const slice = trace.sliceEntries(1, -1);
+ expect(slice.findLastLowerEntry(time9)).toBeUndefined();
+ expect(slice.findLastLowerEntry(time10)).toBeUndefined();
+ expect(slice.findLastLowerEntry(time11)).toBeUndefined();
+ expect(slice.findLastLowerEntry(time12)?.getValue()).toEqual('entry-2');
+ expect(slice.findLastLowerEntry(time13)?.getValue()).toEqual('entry-3');
+ expect(slice.findLastLowerEntry(time14)?.getValue()).toEqual('entry-3');
+ expect(slice.findLastLowerEntry(time15)?.getValue()).toEqual('entry-3');
+
+ // full trace
+ expect(trace.findLastLowerEntry(time9)).toBeUndefined();
+ expect(trace.findLastLowerEntry(time10)).toBeUndefined();
+ expect(trace.findLastLowerEntry(time11)?.getValue()).toEqual('entry-0');
+ expect(trace.findLastLowerEntry(time12)?.getValue()).toEqual('entry-2');
+ expect(trace.findLastLowerEntry(time13)?.getValue()).toEqual('entry-3');
+ expect(trace.findLastLowerEntry(time14)?.getValue()).toEqual('entry-4');
+ expect(trace.findLastLowerEntry(time15)?.getValue()).toEqual('entry-4');
+ });
+
+ // Hint: look at frame mapping specified in test's set up to fully understand the assertions
+ it('sliceEntries()', () => {
+ const slice = trace.sliceEntries(1, 4);
+
+ const expectedEntriesFull = ['entry-1', 'entry-2', 'entry-3'];
+ const expectedFramesEmpty = new Map<AbsoluteFrameIndex, string[]>();
+ const expectedFramesFull = new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ]);
+
+ // empty
+ {
+ expect(TraceUtils.extractFrames(slice.sliceEntries(1, 1))).toEqual(expectedFramesEmpty);
+ expect(TraceUtils.extractEntries(slice.sliceEntries(1, 1))).toEqual([]);
+
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-1, -1))).toEqual(expectedFramesEmpty);
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-1, -1))).toEqual([]);
+
+ expect(TraceUtils.extractFrames(slice.sliceEntries(2, 1))).toEqual(expectedFramesEmpty);
+ expect(TraceUtils.extractEntries(slice.sliceEntries(2, 1))).toEqual([]);
+
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-1, -2))).toEqual(expectedFramesEmpty);
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-1, -2))).toEqual([]);
+ }
+
+ // full
+ {
+ expect(TraceUtils.extractEntries(slice.sliceEntries())).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceEntries())).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(0))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(0))).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(0, 3))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(0, 3))).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-3))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-3))).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-3, 3))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-3, 3))).toEqual(expectedFramesFull);
+ }
+
+ // slice away front (positive index)
+ {
+ expect(TraceUtils.extractEntries(slice.sliceEntries(1))).toEqual(['entry-2', 'entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(2))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(3))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(3))).toEqual(expectedFramesEmpty);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(4))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(4))).toEqual(expectedFramesEmpty);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(1000000))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(1000000))).toEqual(expectedFramesEmpty);
+ }
+
+ // slice away front (negative index)
+ {
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-3))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-3))).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-2))).toEqual(['entry-2', 'entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(-1))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(-1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+ }
+
+ // slice away back (positive index)
+ {
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, 2))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, 2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1', 'entry-2']]])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, 1))).toEqual(['entry-1']);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, 1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1']]])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, 0))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, 0))).toEqual(
+ expectedFramesEmpty
+ );
+ }
+
+ // slice away back (negative index)
+ {
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, -1))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, -1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1', 'entry-2']]])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, -2))).toEqual(['entry-1']);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, -2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1']]])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, -3))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, -3))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, -4))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, -4))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceEntries(undefined, -1000000))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceEntries(undefined, -1000000))).toEqual(
+ expectedFramesEmpty
+ );
+ }
+ });
+
+ // Hint: look at frame mapping specified in test's set up to fully understand the assertions
+ it('sliceTime()', () => {
+ const slice = trace.sliceTime(time11, time13); // drop first + last entries
+
+ const expectedEntriesFull = ['entry-1', 'entry-2', 'entry-3'];
+ const expectedFramesEmpty = new Map<AbsoluteFrameIndex, string[]>();
+ const expectedFramesFull = new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ]);
+
+ // empty
+ {
+ expect(TraceUtils.extractEntries(slice.sliceTime(time11, time11))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time11, time11))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time11, time10))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time11, time10))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time9, time10))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time9, time10))).toEqual(expectedFramesEmpty);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time10, time9))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time10, time9))).toEqual(expectedFramesEmpty);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time14, time15))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time14, time15))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time15, time14))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time15, time14))).toEqual(
+ expectedFramesEmpty
+ );
+ }
+
+ // full
+ {
+ expect(TraceUtils.extractEntries(slice.sliceTime())).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceTime())).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time9))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time9))).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time10))).toEqual(expectedEntriesFull);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time10))).toEqual(expectedFramesFull);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(undefined, time14))).toEqual(
+ expectedEntriesFull
+ );
+ expect(TraceUtils.extractFrames(slice.sliceTime(undefined, time14))).toEqual(
+ expectedFramesFull
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(undefined, time15))).toEqual(
+ expectedEntriesFull
+ );
+ expect(TraceUtils.extractFrames(slice.sliceTime(undefined, time15))).toEqual(
+ expectedFramesFull
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time10, time14))).toEqual(
+ expectedEntriesFull
+ );
+ expect(TraceUtils.extractFrames(slice.sliceTime(time10, time14))).toEqual(expectedFramesFull);
+ }
+
+ // middle
+ {
+ expect(TraceUtils.extractEntries(slice.sliceTime(time12, time13))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time12, time13))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+ }
+
+ // slice away front
+ {
+ expect(TraceUtils.extractEntries(slice.sliceTime(time12))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time12))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time13))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time13))).toEqual(expectedFramesEmpty);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time14))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time14))).toEqual(expectedFramesEmpty);
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(time15))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(time15))).toEqual(expectedFramesEmpty);
+ }
+
+ // slice away back
+ {
+ expect(TraceUtils.extractEntries(slice.sliceTime(undefined, time12))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(undefined, time12))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1', 'entry-2']]])
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(undefined, time11))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(undefined, time11))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(undefined, time10))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(undefined, time10))).toEqual(
+ expectedFramesEmpty
+ );
+
+ expect(TraceUtils.extractEntries(slice.sliceTime(undefined, time9))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceTime(undefined, time9))).toEqual(
+ expectedFramesEmpty
+ );
+ }
+ });
+
+ // Hint: look at frame mapping specified in test's set up to fully understand the assertions
+ it('sliceFrames()', () => {
+ const slice = trace.sliceEntries(1, -1);
+
+ // empty
+ {
+ const expectedEntries = new Array<string>();
+ const expectedFrames = new Map<AbsoluteFrameIndex, string[]>([]);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(1, 1))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(1, 1))).toEqual(expectedFrames);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(5, 1))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(5, 1))).toEqual(expectedFrames);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(3, 2))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(3, 2))).toEqual(expectedFrames);
+ }
+
+ // middle
+ {
+ expect(TraceUtils.extractEntries(slice.sliceFrames(2, 3))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(2, 3))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[2, []]])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(2, 4))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(2, 4))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [2, []],
+ [3, []],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(2, 5))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(2, 5))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ ])
+ );
+ }
+
+ // full
+ {
+ const expectedEntries = ['entry-1', 'entry-2', 'entry-3'];
+ const expectedFrames = new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ]);
+ expect(TraceUtils.extractEntries(slice.sliceFrames())).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames())).toEqual(expectedFrames);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(0))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(0))).toEqual(expectedFrames);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 6))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 6))).toEqual(expectedFrames);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(1, 6))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(1, 6))).toEqual(expectedFrames);
+ expect(TraceUtils.extractEntries(slice.sliceFrames(0, 7))).toEqual(expectedEntries);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(0, 7))).toEqual(expectedFrames);
+ }
+
+ // slice away front
+ {
+ expect(TraceUtils.extractEntries(slice.sliceFrames(2))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(4))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(4))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(5))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(5))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[5, ['entry-3']]])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(6))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(6))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(1000))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(1000))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([])
+ );
+ }
+
+ // slice away back
+ {
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 6))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 6))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 5))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 5))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 4))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 4))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 3))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 3))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ ])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 2))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[1, ['entry-1', 'entry-2']]])
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 1))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+ expect(TraceUtils.extractEntries(slice.sliceFrames(undefined, 0))).toEqual([]);
+ expect(TraceUtils.extractFrames(slice.sliceFrames(undefined, 0))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+ }
+ });
+
+ it('can slice full trace', () => {
+ // entries
+ expect(TraceUtils.extractEntries(trace.sliceEntries(1, 1))).toEqual([]);
+ expect(TraceUtils.extractEntries(trace.sliceEntries())).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceEntries(2))).toEqual([
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceEntries(-3))).toEqual([
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceEntries(undefined, 3))).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceEntries(undefined, -2))).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceEntries(1, 4))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ ]);
+
+ // time
+ const time12 = new RealTimestamp(12n);
+ const time13 = new RealTimestamp(13n);
+ expect(TraceUtils.extractEntries(trace.sliceTime(time12, time12))).toEqual([]);
+ expect(TraceUtils.extractEntries(trace.sliceTime())).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceTime(time12, time13))).toEqual(['entry-3']);
+ expect(TraceUtils.extractEntries(trace.sliceTime(time12))).toEqual(['entry-3', 'entry-4']);
+ expect(TraceUtils.extractEntries(trace.sliceTime(undefined, time12))).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ ]);
+
+ // frames
+ expect(TraceUtils.extractEntries(trace.sliceFrames(1, 1))).toEqual([]);
+ expect(TraceUtils.extractEntries(trace.sliceFrames())).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceFrames(2))).toEqual(['entry-3', 'entry-4']);
+ expect(TraceUtils.extractEntries(trace.sliceFrames(undefined, 5))).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ ]);
+ expect(TraceUtils.extractEntries(trace.sliceFrames(2, 5))).toEqual(['entry-3']);
+ });
+
+ it('can slice empty trace', () => {
+ const empty = trace.sliceEntries(0, 0);
+
+ // entries
+ expect(TraceUtils.extractEntries(empty.sliceEntries())).toEqual([]);
+ expect(TraceUtils.extractEntries(empty.sliceEntries(1))).toEqual([]);
+ expect(TraceUtils.extractEntries(empty.sliceEntries(1, 2))).toEqual([]);
+
+ // time
+ const time12 = new RealTimestamp(12n);
+ const time13 = new RealTimestamp(13n);
+ expect(TraceUtils.extractEntries(empty.sliceTime())).toEqual([]);
+ expect(TraceUtils.extractEntries(empty.sliceTime(time12))).toEqual([]);
+ expect(TraceUtils.extractEntries(empty.sliceTime(time12, time13))).toEqual([]);
+
+ // frames
+ expect(TraceUtils.extractEntries(empty.sliceFrames())).toEqual([]);
+ expect(TraceUtils.extractEntries(empty.sliceFrames(1))).toEqual([]);
+ expect(TraceUtils.extractEntries(empty.sliceFrames(1, 2))).toEqual([]);
+ });
+
+ it('forEachEntry()', () => {
+ expect(TraceUtils.extractEntries(trace)).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ });
+
+ it('forEachTimestamp()', () => {
+ expect(TraceUtils.extractTimestamps(trace)).toEqual([time10, time11, time11, time12, time13]);
+ expect(TraceUtils.extractTimestamps(trace.sliceEntries(1, -1))).toEqual([
+ time11,
+ time11,
+ time12,
+ ]);
+ });
+
+ // Hint: look at frame mapping specified in test's set up to fully understand the assertions
+ it('forEachFrame()', () => {
+ // full trace
+ {
+ const expected = new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-0']],
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ [5, ['entry-3']],
+ [6, ['entry-4']],
+ ]);
+ expect(TraceUtils.extractFrames(trace)).toEqual(expected);
+ }
+ // slice
+ {
+ const slice = trace.sliceFrames(1, 5);
+ const expected = new Map<AbsoluteFrameIndex, string[]>([
+ [1, ['entry-1', 'entry-2']],
+ [2, []],
+ [3, []],
+ [4, ['entry-3']],
+ ]);
+ expect(TraceUtils.extractFrames(slice)).toEqual(expected);
+ }
+ });
+
+ it('updates frames range when slicing', () => {
+ expect(trace.sliceEntries(0).getFramesRange()).toEqual({start: 0, end: 7});
+ expect(trace.sliceEntries(1).getFramesRange()).toEqual({start: 1, end: 7});
+ expect(trace.sliceEntries(2).getFramesRange()).toEqual({start: 1, end: 7});
+ expect(trace.sliceEntries(3).getFramesRange()).toEqual({start: 4, end: 7});
+ expect(trace.sliceEntries(4).getFramesRange()).toEqual({start: 6, end: 7});
+ expect(trace.sliceEntries(5).getFramesRange()).toEqual(undefined);
+
+ expect(trace.sliceEntries(undefined, 5).getFramesRange()).toEqual({start: 0, end: 7});
+ expect(trace.sliceEntries(undefined, 4).getFramesRange()).toEqual({start: 0, end: 6});
+ expect(trace.sliceEntries(undefined, 3).getFramesRange()).toEqual({start: 0, end: 2});
+ expect(trace.sliceEntries(undefined, 2).getFramesRange()).toEqual({start: 0, end: 2});
+ expect(trace.sliceEntries(undefined, 1).getFramesRange()).toEqual({start: 0, end: 1});
+ expect(trace.sliceEntries(undefined, 0).getFramesRange()).toEqual(undefined);
+ });
+
+ it('can handle some trace entries with unavailable frame info', () => {
+ // Entry: 0 1 2 3 4
+ // | |
+ // Frame: 0 2
+ // Time: 10 11 12 13 14
+ const trace = new TraceBuilder<string>()
+ .setEntries(['entry-0', 'entry-1', 'entry-2', 'entry-3', 'entry-4'])
+ .setTimestamps([time10, time11, time12, time13, time14])
+ .setFrame(1, 0)
+ .setFrame(3, 2)
+ .build();
+
+ // Slice entries
+ expect(TraceUtils.extractEntries(trace.sliceEntries())).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceEntries())).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-1']],
+ [1, []],
+ [2, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceEntries(1))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceEntries(1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-1']],
+ [1, []],
+ [2, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceEntries(2))).toEqual([
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceEntries(2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[2, ['entry-3']]])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceEntries(3))).toEqual(['entry-3', 'entry-4']);
+ expect(TraceUtils.extractFrames(trace.sliceEntries(3))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[2, ['entry-3']]])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceEntries(4))).toEqual(['entry-4']);
+ expect(TraceUtils.extractFrames(trace.sliceEntries(4))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+
+ // Slice time
+ expect(TraceUtils.extractEntries(trace.sliceTime())).toEqual([
+ 'entry-0',
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceTime())).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-1']],
+ [1, []],
+ [2, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceTime(time11))).toEqual([
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceTime(time11))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-1']],
+ [1, []],
+ [2, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceTime(time12))).toEqual([
+ 'entry-2',
+ 'entry-3',
+ 'entry-4',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceTime(time12))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[2, ['entry-3']]])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceTime(time13))).toEqual(['entry-3', 'entry-4']);
+ expect(TraceUtils.extractFrames(trace.sliceTime(time13))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([[2, ['entry-3']]])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceTime(time14))).toEqual(['entry-4']);
+ expect(TraceUtils.extractFrames(trace.sliceTime(time14))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+
+ // Slice frames
+ expect(TraceUtils.extractEntries(trace.sliceFrames())).toEqual([
+ 'entry-1',
+ 'entry-2',
+ 'entry-3',
+ ]);
+ expect(TraceUtils.extractFrames(trace.sliceFrames())).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-1']],
+ [1, []],
+ [2, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceFrames(1))).toEqual(['entry-3']);
+ expect(TraceUtils.extractFrames(trace.sliceFrames(1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [1, []],
+ [2, ['entry-3']],
+ ])
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceFrames(undefined, 2))).toEqual(['entry-1']);
+ expect(TraceUtils.extractFrames(trace.sliceFrames(undefined, 2))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>([
+ [0, ['entry-1']],
+ [1, []],
+ ])
+ );
+ });
+
+ it('can handle unavailable frame info', () => {
+ const trace = new TraceBuilder<string>()
+ .setTimestamps([time10, time11, time12])
+ .setEntries(['entry-0', 'entry-1', 'entry-2'])
+ .setFrameMap(undefined)
+ .build();
+
+ expect(trace.getEntry(0).getValue()).toEqual('entry-0');
+ expect(TraceUtils.extractEntries(trace)).toEqual(['entry-0', 'entry-1', 'entry-2']);
+ expect(TraceUtils.extractEntries(trace.sliceEntries(1, 2))).toEqual(['entry-1']);
+ expect(TraceUtils.extractEntries(trace.sliceTime(time11, time12))).toEqual(['entry-1']);
+
+ expect(() => {
+ trace.getFrame(0);
+ }).toThrow();
+ expect(() => {
+ trace.sliceFrames(0, 1000);
+ }).toThrow();
+ });
+
+ it('can handle empty frame info', () => {
+ // empty trace
+ {
+ const trace = new TraceBuilder<string>()
+ .setEntries([])
+ .setTimestamps([])
+ .setFrameMap(new FrameMapBuilder(0, 0).build())
+ .build();
+
+ expect(TraceUtils.extractEntries(trace)).toEqual([]);
+ expect(TraceUtils.extractFrames(trace)).toEqual(new Map<AbsoluteFrameIndex, string[]>());
+
+ expect(TraceUtils.extractEntries(trace.sliceEntries(1))).toEqual([]);
+ expect(TraceUtils.extractFrames(trace.sliceEntries(1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceTime(time11))).toEqual([]);
+ expect(TraceUtils.extractFrames(trace.sliceTime(time11))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceFrames())).toEqual([]);
+ expect(TraceUtils.extractFrames(trace.sliceFrames())).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+ }
+ // non-empty trace
+ {
+ const trace = new TraceBuilder<string>()
+ .setEntries(['entry-0', 'entry-1', 'entry-2'])
+ .setTimestamps([time10, time11, time12])
+ .setFrameMap(new FrameMapBuilder(3, 0).build())
+ .build();
+
+ expect(TraceUtils.extractEntries(trace)).toEqual(['entry-0', 'entry-1', 'entry-2']);
+ expect(TraceUtils.extractFrames(trace)).toEqual(new Map<AbsoluteFrameIndex, string[]>());
+
+ expect(TraceUtils.extractEntries(trace.sliceEntries(1))).toEqual(['entry-1', 'entry-2']);
+ expect(TraceUtils.extractFrames(trace.sliceEntries(1))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceTime(time11))).toEqual(['entry-1', 'entry-2']);
+ expect(TraceUtils.extractFrames(trace.sliceTime(time11))).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+
+ expect(TraceUtils.extractEntries(trace.sliceFrames())).toEqual([]);
+ expect(TraceUtils.extractFrames(trace.sliceFrames())).toEqual(
+ new Map<AbsoluteFrameIndex, string[]>()
+ );
+ }
+ });
+});
diff --git a/tools/winscope/src/trace/trace_tree_node.ts b/tools/winscope/src/trace/trace_tree_node.ts
new file mode 100644
index 0000000..9116cf4
--- /dev/null
+++ b/tools/winscope/src/trace/trace_tree_node.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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 interface TraceTreeNode {
+ children: TraceTreeNode[];
+ name: string;
+ kind: string;
+ stableId: string;
+ parent?: TraceTreeNode;
+ displays?: TraceTreeNode[];
+ windowStates?: TraceTreeNode[];
+ client?: any;
+ inputMethodService?: any;
+ inputMethodManagerService?: any;
+ where?: string;
+ elapsedRealtimeNanos?: number | bigint;
+ clockTimeNanos?: number | bigint;
+ shortName?: string;
+ type?: string;
+ id?: string | number;
+ layerId?: number;
+ displayId?: number;
+ stackId?: number;
+ isVisible?: boolean;
+ isMissing?: boolean;
+ hwcCompositionType?: number;
+ zOrderRelativeOfId?: number;
+ isRootLayer?: boolean;
+ diffType?: string;
+ skip?: any;
+ obj?: any;
+ proto?: any;
+ equals?: any;
+}
diff --git a/tools/winscope/src/trace/trace_type.ts b/tools/winscope/src/trace/trace_type.ts
new file mode 100644
index 0000000..7264af7
--- /dev/null
+++ b/tools/winscope/src/trace/trace_type.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 {Cuj, Event, Transition} from 'trace/flickerlib/common';
+import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry';
+import {WindowManagerState} from './flickerlib/windows/WindowManagerState';
+import {LogMessage} from './protolog';
+import {ScreenRecordingTraceEntry} from './screen_recording';
+
+export enum TraceType {
+ ACCESSIBILITY,
+ WINDOW_MANAGER,
+ SURFACE_FLINGER,
+ SCREEN_RECORDING,
+ TRANSACTIONS,
+ TRANSACTIONS_LEGACY,
+ WAYLAND,
+ WAYLAND_DUMP,
+ PROTO_LOG,
+ SYSTEM_UI,
+ LAUNCHER,
+ INPUT_METHOD_CLIENTS,
+ INPUT_METHOD_MANAGER_SERVICE,
+ INPUT_METHOD_SERVICE,
+ EVENT_LOG,
+ WM_TRANSITION,
+ SHELL_TRANSITION,
+ TRANSITION,
+ CUJS,
+ TAG,
+ ERROR,
+ TEST_TRACE_STRING,
+ TEST_TRACE_NUMBER,
+}
+
+export interface TraceEntryTypeMap {
+ [TraceType.ACCESSIBILITY]: object;
+ [TraceType.LAUNCHER]: object;
+ [TraceType.PROTO_LOG]: LogMessage;
+ [TraceType.SURFACE_FLINGER]: LayerTraceEntry;
+ [TraceType.SCREEN_RECORDING]: ScreenRecordingTraceEntry;
+ [TraceType.SYSTEM_UI]: object;
+ [TraceType.TRANSACTIONS]: object;
+ [TraceType.TRANSACTIONS_LEGACY]: object;
+ [TraceType.WAYLAND]: object;
+ [TraceType.WAYLAND_DUMP]: object;
+ [TraceType.WINDOW_MANAGER]: WindowManagerState;
+ [TraceType.INPUT_METHOD_CLIENTS]: object;
+ [TraceType.INPUT_METHOD_MANAGER_SERVICE]: object;
+ [TraceType.INPUT_METHOD_SERVICE]: object;
+ [TraceType.EVENT_LOG]: Event;
+ [TraceType.WM_TRANSITION]: object;
+ [TraceType.SHELL_TRANSITION]: object;
+ [TraceType.TRANSITION]: Transition;
+ [TraceType.CUJS]: Cuj;
+ [TraceType.TAG]: object;
+ [TraceType.ERROR]: object;
+ [TraceType.TEST_TRACE_STRING]: string;
+ [TraceType.TEST_TRACE_NUMBER]: number;
+}
diff --git a/tools/winscope/src/trace/traces.ts b/tools/winscope/src/trace/traces.ts
new file mode 100644
index 0000000..eabdad4
--- /dev/null
+++ b/tools/winscope/src/trace/traces.ts
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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 {AbsoluteFrameIndex} from './index_types';
+import {Timestamp} from './timestamp';
+import {Trace} from './trace';
+import {TraceEntryTypeMap, TraceType} from './trace_type';
+
+export class Traces {
+ private traces = new Map<TraceType, Trace<{}>>();
+
+ setTrace<T extends TraceType>(type: T, trace: Trace<TraceEntryTypeMap[T]>) {
+ this.traces.set(type, trace);
+ }
+
+ getTrace<T extends TraceType>(type: T): Trace<TraceEntryTypeMap[T]> | undefined {
+ return this.traces.get(type) as Trace<TraceEntryTypeMap[T]> | undefined;
+ }
+
+ deleteTrace<T extends TraceType>(type: T) {
+ this.traces.delete(type);
+ }
+
+ sliceTime(start?: Timestamp, end?: Timestamp): Traces {
+ const slice = new Traces();
+ this.traces.forEach((trace, type) => {
+ slice.setTrace(type, trace.sliceTime(start, end));
+ });
+ return slice;
+ }
+
+ sliceFrames(start?: AbsoluteFrameIndex, end?: AbsoluteFrameIndex): Traces {
+ const slice = new Traces();
+ this.traces.forEach((trace, type) => {
+ slice.setTrace(type, trace.sliceFrames(start, end));
+ });
+ return slice;
+ }
+
+ forEachTrace(callback: (trace: Trace<{}>, type: TraceType) => void): void {
+ this.traces.forEach((trace, type) => {
+ callback(trace, type);
+ });
+ }
+
+ forEachFrame(callback: (traces: Traces, index: AbsoluteFrameIndex) => void): void {
+ let startFrameIndex: AbsoluteFrameIndex = Number.MAX_VALUE;
+ let endFrameIndex: AbsoluteFrameIndex = Number.MIN_VALUE;
+
+ this.traces.forEach((trace) => {
+ const framesRange = trace.getFramesRange();
+ if (framesRange && framesRange.start < framesRange.end) {
+ startFrameIndex = Math.min(startFrameIndex, framesRange.start);
+ endFrameIndex = Math.max(endFrameIndex, framesRange.end);
+ }
+ });
+
+ for (let i = startFrameIndex; i < endFrameIndex; ++i) {
+ callback(this.sliceFrames(i, i + 1), i);
+ }
+ }
+}
diff --git a/tools/winscope/src/trace/traces_test.ts b/tools/winscope/src/trace/traces_test.ts
new file mode 100644
index 0000000..c0c70a6
--- /dev/null
+++ b/tools/winscope/src/trace/traces_test.ts
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2023 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 {TracesBuilder} from 'test/unit/traces_builder';
+import {TracesUtils} from 'test/unit/traces_utils';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {TraceUtils} from 'test/unit/trace_utils';
+import {assertDefined} from '../common/assert_utils';
+import {FrameMapBuilder} from './frame_map_builder';
+import {AbsoluteFrameIndex} from './index_types';
+import {RealTimestamp} from './timestamp';
+import {Traces} from './traces';
+import {TraceType} from './trace_type';
+
+describe('Traces', () => {
+ let traces: Traces;
+
+ const time1 = new RealTimestamp(1n);
+ const time2 = new RealTimestamp(2n);
+ const time3 = new RealTimestamp(3n);
+ const time4 = new RealTimestamp(4n);
+ const time5 = new RealTimestamp(5n);
+ const time6 = new RealTimestamp(6n);
+ const time7 = new RealTimestamp(7n);
+ const time8 = new RealTimestamp(8n);
+ const time9 = new RealTimestamp(9n);
+ const time10 = new RealTimestamp(10n);
+
+ let extractedEntriesEmpty: Map<TraceType, Array<{}>>;
+ let extractedEntriesFull: Map<TraceType, Array<{}>>;
+ let extractedFramesEmpty: Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>;
+ let extractedFramesFull: Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>;
+
+ beforeAll(() => {
+ // Time: 1 2 3 4 5 6 7 8 9 10
+ //
+ // TEST_TRACE_STRING: 0 1--2 3 4
+ // \ \ \ \
+ // \ \ \ \
+ // TEST_TRACE_NUMBER: 0 1 2--3 4
+ // \ \ \ \
+ // \ \ \ \
+ // Frame on screen: 0 1 2 3---4
+ traces = new Traces();
+ traces.setTrace(
+ TraceType.TEST_TRACE_STRING,
+ new TraceBuilder<string>()
+ .setType(TraceType.TEST_TRACE_STRING)
+ .setEntries(['0', '1', '2', '3', '4'])
+ .setTimestamps([time1, time3, time4, time6, time9])
+ .setFrame(0, 0)
+ .setFrame(1, 1)
+ .setFrame(2, 1)
+ .setFrame(3, 2)
+ .setFrame(4, 3)
+ .setFrame(4, 4)
+ .build()
+ );
+ traces.setTrace(
+ TraceType.TEST_TRACE_NUMBER,
+ new TraceBuilder<number>()
+ .setType(TraceType.TEST_TRACE_NUMBER)
+ .setEntries([0, 1, 2, 3, 4])
+ .setTimestamps([time2, time5, time7, time8, time10])
+ .setFrame(0, 0)
+ .setFrame(1, 1)
+ .setFrame(2, 2)
+ .setFrame(3, 2)
+ .setFrame(4, 3)
+ .setFrame(4, 4)
+ .build()
+ );
+
+ extractedEntriesEmpty = new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, []],
+ [TraceType.TEST_TRACE_NUMBER, []],
+ ]);
+
+ extractedEntriesFull = new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['0', '1', '2', '3', '4']],
+ [TraceType.TEST_TRACE_NUMBER, [0, 1, 2, 3, 4]],
+ ]);
+
+ extractedFramesEmpty = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+
+ extractedFramesFull = new Map<AbsoluteFrameIndex, Map<TraceType, Array<{}>>>();
+ extractedFramesFull.set(
+ 0,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['0']],
+ [TraceType.TEST_TRACE_NUMBER, [0]],
+ ])
+ );
+ extractedFramesFull.set(
+ 1,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['1', '2']],
+ [TraceType.TEST_TRACE_NUMBER, [1]],
+ ])
+ );
+ extractedFramesFull.set(
+ 2,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['3']],
+ [TraceType.TEST_TRACE_NUMBER, [2, 3]],
+ ])
+ );
+ extractedFramesFull.set(
+ 3,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['4']],
+ [TraceType.TEST_TRACE_NUMBER, [4]],
+ ])
+ );
+ extractedFramesFull.set(
+ 4,
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['4']],
+ [TraceType.TEST_TRACE_NUMBER, [4]],
+ ])
+ );
+ });
+
+ it('getTrace()', () => {
+ expect(
+ TraceUtils.extractEntries(assertDefined(traces.getTrace(TraceType.TEST_TRACE_STRING)))
+ ).toEqual(extractedEntriesFull.get(TraceType.TEST_TRACE_STRING) as string[]);
+ expect(
+ TraceUtils.extractEntries(assertDefined(traces.getTrace(TraceType.TEST_TRACE_NUMBER)))
+ ).toEqual(extractedEntriesFull.get(TraceType.TEST_TRACE_NUMBER) as number[]);
+ expect(traces.getTrace(TraceType.SURFACE_FLINGER)).toBeUndefined();
+ });
+
+ it('sliceTime()', () => {
+ // empty
+ {
+ const slice = traces.sliceTime(time3, time3);
+ expect(TracesUtils.extractEntries(slice)).toEqual(extractedEntriesEmpty);
+ }
+ // full
+ {
+ const slice = traces.sliceTime();
+ expect(TracesUtils.extractEntries(slice)).toEqual(extractedEntriesFull);
+ }
+ // middle
+ {
+ const slice = traces.sliceTime(time4, time8);
+ expect(TracesUtils.extractEntries(slice)).toEqual(
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['2', '3']],
+ [TraceType.TEST_TRACE_NUMBER, [1, 2]],
+ ])
+ );
+ }
+ // slice away front
+ {
+ const slice = traces.sliceTime(time8);
+ expect(TracesUtils.extractEntries(slice)).toEqual(
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['4']],
+ [TraceType.TEST_TRACE_NUMBER, [3, 4]],
+ ])
+ );
+ }
+ // slice away back
+ {
+ const slice = traces.sliceTime(undefined, time8);
+ expect(TracesUtils.extractEntries(slice)).toEqual(
+ new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['0', '1', '2', '3']],
+ [TraceType.TEST_TRACE_NUMBER, [0, 1, 2]],
+ ])
+ );
+ }
+ });
+
+ it('sliceFrames()', () => {
+ // empty
+ {
+ const slice = traces.sliceFrames(1, 1);
+ expect(TracesUtils.extractFrames(slice)).toEqual(extractedFramesEmpty);
+ }
+ // full
+ {
+ const slice = traces.sliceFrames();
+ expect(TracesUtils.extractFrames(slice)).toEqual(extractedFramesFull);
+ }
+ // middle
+ {
+ const slice = traces.sliceFrames(1, 4);
+ const expectedFrames = structuredClone(extractedFramesFull);
+ expectedFrames.delete(0);
+ expectedFrames.delete(4);
+ expect(TracesUtils.extractFrames(slice)).toEqual(expectedFrames);
+ }
+ // slice away front
+ {
+ const slice = traces.sliceFrames(2);
+ const expectedFrames = structuredClone(extractedFramesFull);
+ expectedFrames.delete(0);
+ expectedFrames.delete(1);
+ expect(TracesUtils.extractFrames(slice)).toEqual(expectedFrames);
+ }
+ // slice away back
+ {
+ const slice = traces.sliceFrames(undefined, 2);
+ const expectedFrames = structuredClone(extractedFramesFull);
+ expectedFrames.delete(2);
+ expectedFrames.delete(3);
+ expectedFrames.delete(4);
+ expect(TracesUtils.extractFrames(slice)).toEqual(expectedFrames);
+ }
+ });
+
+ it('forEachTrace()', () => {
+ traces.forEachTrace((trace) => {
+ const expectedEntries = extractedEntriesFull.get(trace.type) as Array<{}>;
+ const actualEntries = TraceUtils.extractEntries(trace);
+ expect(actualEntries).toEqual(expectedEntries);
+ });
+ });
+
+ it('forEachFrame()', () => {
+ expect(TracesUtils.extractFrames(traces)).toEqual(extractedFramesFull);
+ });
+
+ it('it supports empty traces', () => {
+ const traces = new TracesBuilder()
+ .setEntries(TraceType.TEST_TRACE_STRING, [])
+ .setFrameMap(TraceType.TEST_TRACE_STRING, new FrameMapBuilder(0, 0).build())
+
+ .setEntries(TraceType.TEST_TRACE_NUMBER, [])
+ .setFrameMap(TraceType.TEST_TRACE_NUMBER, new FrameMapBuilder(0, 0).build())
+ .build();
+
+ expect(TracesUtils.extractEntries(traces)).toEqual(extractedEntriesEmpty);
+ expect(TracesUtils.extractFrames(traces)).toEqual(extractedFramesEmpty);
+
+ expect(TracesUtils.extractEntries(traces.sliceTime(time1, time10))).toEqual(
+ extractedEntriesEmpty
+ );
+ expect(TracesUtils.extractFrames(traces.sliceTime(time1, time10))).toEqual(
+ extractedFramesEmpty
+ );
+
+ expect(TracesUtils.extractEntries(traces.sliceFrames(0, 10))).toEqual(extractedEntriesEmpty);
+ expect(TracesUtils.extractFrames(traces.sliceFrames(0, 10))).toEqual(extractedFramesEmpty);
+ });
+
+ it('it supports unavailable frame mapping', () => {
+ const traces = new TracesBuilder()
+ .setEntries(TraceType.TEST_TRACE_STRING, ['entry-0'])
+ .setTimestamps(TraceType.TEST_TRACE_STRING, [time1])
+ .setFrameMap(TraceType.TEST_TRACE_STRING, undefined)
+
+ .setEntries(TraceType.TEST_TRACE_NUMBER, [0])
+ .setTimestamps(TraceType.TEST_TRACE_NUMBER, [time1])
+ .setFrameMap(TraceType.TEST_TRACE_NUMBER, undefined)
+ .build();
+
+ const expectedEntries = new Map<TraceType, Array<{}>>([
+ [TraceType.TEST_TRACE_STRING, ['entry-0']],
+ [TraceType.TEST_TRACE_NUMBER, [0]],
+ ]);
+
+ expect(TracesUtils.extractEntries(traces)).toEqual(expectedEntries);
+ expect(TracesUtils.extractEntries(traces.sliceTime())).toEqual(expectedEntries);
+ expect(() => {
+ traces.sliceFrames();
+ }).toThrow();
+ expect(() => {
+ TracesUtils.extractFrames(traces);
+ }).toThrow();
+ });
+});
diff --git a/tools/winscope/src/trace_collection/connection.ts b/tools/winscope/src/trace_collection/connection.ts
new file mode 100644
index 0000000..b91f1d2
--- /dev/null
+++ b/tools/winscope/src/trace_collection/connection.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022, 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 {ProxyClient} from 'trace_collection/proxy_client';
+import {ConfigMap} from './trace_collection_utils';
+
+export interface Device {
+ [key: string]: DeviceProperties;
+}
+
+export interface DeviceProperties {
+ authorised: boolean;
+ model: string;
+}
+
+export interface Connection {
+ adbSuccess: () => boolean;
+ setProxyKey?(key: string): any;
+ devices(): Device;
+ selectedDevice(): DeviceProperties;
+ selectedDeviceId(): string;
+ restart(): any;
+ selectDevice(id: string): any;
+ state(): any;
+ onConnectChange(newState: any): any;
+ resetLastDevice(): any;
+ isDevicesState(): boolean;
+ isStartTraceState(): boolean;
+ isErrorState(): boolean;
+ isEndTraceState(): boolean;
+ isLoadDataState(): boolean;
+ isConnectingState(): boolean;
+ throwNoTargetsError(): any;
+ startTrace(
+ reqEnableConfig?: string[],
+ reqSelectedSfConfig?: ConfigMap,
+ reqSelectedWmConfig?: ConfigMap
+ ): any;
+ endTrace(): any;
+ adbData(): File[];
+ dumpState(): any;
+ proxy?: ProxyClient;
+}
diff --git a/tools/winscope/src/trace_collection/proxy_client.ts b/tools/winscope/src/trace_collection/proxy_client.ts
new file mode 100644
index 0000000..02d33f3
--- /dev/null
+++ b/tools/winscope/src/trace_collection/proxy_client.ts
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2022, 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 {OnProgressUpdateType} from 'common/function_utils';
+import {PersistentStore} from 'common/persistent_store';
+import {Device} from './connection';
+import {ConfigMap} from './trace_collection_utils';
+
+export enum ProxyState {
+ ERROR = 0,
+ CONNECTING = 1,
+ NO_PROXY = 2,
+ INVALID_VERSION = 3,
+ UNAUTH = 4,
+ DEVICES = 5,
+ START_TRACE = 6,
+ END_TRACE = 7,
+ LOAD_DATA = 8,
+}
+
+export enum ProxyEndpoint {
+ DEVICES = '/devices/',
+ START_TRACE = '/start/',
+ END_TRACE = '/end/',
+ ENABLE_CONFIG_TRACE = '/configtrace/',
+ SELECTED_WM_CONFIG_TRACE = '/selectedwmconfigtrace/',
+ SELECTED_SF_CONFIG_TRACE = '/selectedsfconfigtrace/',
+ DUMP = '/dump/',
+ FETCH = '/fetch/',
+ STATUS = '/status/',
+ CHECK_WAYLAND = '/checkwayland/',
+}
+
+// from here, all requests to the proxy are made
+class ProxyRequest {
+ // List of trace we are actively tracing
+ private tracingTraces: string[] | undefined;
+
+ async call(
+ method: string,
+ path: string,
+ onSuccess: ((request: XMLHttpRequest) => void | Promise<void>) | undefined,
+ type?: XMLHttpRequest['responseType'],
+ jsonRequest: any = null
+ ): Promise<void> {
+ return new Promise((resolve) => {
+ const request = new XMLHttpRequest();
+ const client = proxyClient;
+ request.onreadystatechange = async function () {
+ if (this.readyState !== XMLHttpRequest.DONE) {
+ return;
+ }
+ if (this.status === XMLHttpRequest.UNSENT) {
+ client.setState(ProxyState.NO_PROXY);
+ resolve();
+ } else if (this.status === 200) {
+ if (this.getResponseHeader('Winscope-Proxy-Version') !== client.VERSION) {
+ client.setState(ProxyState.INVALID_VERSION);
+ resolve();
+ } else if (onSuccess) {
+ try {
+ await onSuccess(this);
+ } catch (err) {
+ console.error(err);
+ proxyClient.setState(
+ ProxyState.ERROR,
+ `Error handling request response:\n${err}\n\n` +
+ `Request:\n ${request.responseText}`
+ );
+ resolve();
+ }
+ }
+ resolve();
+ } else if (this.status === 403) {
+ client.setState(ProxyState.UNAUTH);
+ resolve();
+ } else {
+ if (this.responseType === 'text' || !this.responseType) {
+ client.errorText = this.responseText;
+ } else if (this.responseType === 'arraybuffer') {
+ client.errorText = String.fromCharCode.apply(null, new Array(this.response));
+ }
+ client.setState(ProxyState.ERROR, client.errorText);
+ resolve();
+ }
+ };
+ request.responseType = type || '';
+ request.open(method, client.WINSCOPE_PROXY_URL + path);
+ const lastKey = client.store.get('adb.proxyKey');
+ if (lastKey !== null) {
+ client.proxyKey = lastKey;
+ }
+ request.setRequestHeader('Winscope-Token', client.proxyKey);
+ if (jsonRequest) {
+ const json = JSON.stringify(jsonRequest);
+ request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
+ request.send(json);
+ } else {
+ request.send();
+ }
+ });
+ }
+
+ async getDevices(view: any) {
+ await proxyRequest.call('GET', ProxyEndpoint.DEVICES, proxyRequest.onSuccessGetDevices);
+ }
+
+ async setEnabledConfig(view: any, req: string[]) {
+ await proxyRequest.call(
+ 'POST',
+ `${ProxyEndpoint.ENABLE_CONFIG_TRACE}${view.proxy.selectedDevice}/`,
+ undefined,
+ undefined,
+ req
+ );
+ }
+
+ async setSelectedConfig(endpoint: ProxyEndpoint, view: any, req: ConfigMap) {
+ await proxyRequest.call(
+ 'POST',
+ `${endpoint}${view.proxy.selectedDevice}/`,
+ undefined,
+ undefined,
+ req
+ );
+ }
+
+ async startTrace(view: any, requestedTraces: string[]) {
+ this.tracingTraces = requestedTraces;
+ await proxyRequest.call(
+ 'POST',
+ `${ProxyEndpoint.START_TRACE}${view.proxy.selectedDevice}/`,
+ (request: XMLHttpRequest) => {
+ view.keepAliveTrace(view);
+ },
+ undefined,
+ requestedTraces
+ );
+ }
+
+ async endTrace(view: any, progressCallback: OnProgressUpdateType): Promise<void> {
+ const requestedTraces = this.tracingTraces;
+ this.tracingTraces = undefined;
+ if (requestedTraces === undefined) {
+ throw Error('Trace no started before stopping');
+ }
+ await proxyRequest.call(
+ 'POST',
+ `${ProxyEndpoint.END_TRACE}${view.proxy.selectedDevice}/`,
+ async (request: XMLHttpRequest) => {
+ await proxyClient.updateAdbData(requestedTraces, 'trace', progressCallback);
+ }
+ );
+ }
+
+ async keepTraceAlive(view: any) {
+ await this.call(
+ 'GET',
+ `${ProxyEndpoint.STATUS}${view.proxy.selectedDevice}/`,
+ (request: XMLHttpRequest) => {
+ if (request.responseText !== 'True') {
+ view.endTrace();
+ } else if (view.keep_alive_worker === null) {
+ view.keep_alive_worker = setInterval(view.keepAliveTrace, 1000, view);
+ }
+ }
+ );
+ }
+
+ async dumpState(view: any, requestedDumps: string[], progressCallback: OnProgressUpdateType) {
+ await proxyRequest.call(
+ 'POST',
+ `${ProxyEndpoint.DUMP}${view.proxy.selectedDevice}/`,
+ async (request: XMLHttpRequest) => {
+ await proxyClient.updateAdbData(requestedDumps, 'dump', progressCallback);
+ },
+ undefined,
+ requestedDumps
+ );
+ }
+
+ onSuccessGetDevices = (request: XMLHttpRequest) => {
+ const client = proxyClient;
+ try {
+ client.devices = JSON.parse(request.responseText);
+ const last = client.store.get('adb.lastDevice');
+ if (last && client.devices[last] && client.devices[last].authorised) {
+ client.selectDevice(last);
+ } else {
+ if (client.refresh_worker === null) {
+ client.refresh_worker = setInterval(client.getDevices, 1000);
+ }
+ client.setState(ProxyState.DEVICES);
+ }
+ } catch (err) {
+ console.error(err);
+ client.errorText = request.responseText;
+ client.setState(ProxyState.ERROR, client.errorText);
+ }
+ };
+
+ async fetchFiles(dev: string, adbParams: AdbParams): Promise<void> {
+ const files = adbParams.files;
+ const idx = adbParams.idx;
+
+ await proxyRequest.call(
+ 'GET',
+ `${ProxyEndpoint.FETCH}${dev}/${files[idx]}/`,
+ async (request: XMLHttpRequest) => {
+ try {
+ const enc = new TextDecoder('utf-8');
+ const resp = enc.decode(request.response);
+ const filesByType = JSON.parse(resp);
+
+ for (const filetype of Object.keys(filesByType)) {
+ const files = filesByType[filetype];
+ for (const encodedFileBuffer of files) {
+ const buffer = Uint8Array.from(atob(encodedFileBuffer), (c) => c.charCodeAt(0));
+ const blob = new Blob([buffer]);
+ const newFile = new File([blob], filetype);
+ proxyClient.adbData.push(newFile);
+ }
+ }
+ } catch (error) {
+ proxyClient.setState(ProxyState.ERROR, request.responseText);
+ throw error;
+ }
+ },
+ 'arraybuffer'
+ );
+ }
+}
+export const proxyRequest = new ProxyRequest();
+
+interface AdbParams {
+ files: string[];
+ idx: number;
+ traceType: string;
+}
+
+// stores all the changing variables from proxy and sets up calls from ProxyRequest
+export class ProxyClient {
+ readonly WINSCOPE_PROXY_URL = 'http://localhost:5544';
+ readonly VERSION = '1.0';
+ state: ProxyState = ProxyState.CONNECTING;
+ stateChangeListeners: Array<{(param: ProxyState, errorText: string): void}> = [];
+ refresh_worker: NodeJS.Timer | null = null;
+ devices: Device = {};
+ selectedDevice = '';
+ errorText = '';
+ adbData: File[] = [];
+ proxyKey = '';
+ lastDevice = '';
+ store = new PersistentStore();
+
+ setState(state: ProxyState, errorText = '') {
+ this.state = state;
+ this.errorText = errorText;
+ for (const listener of this.stateChangeListeners) {
+ listener(state, errorText);
+ }
+ }
+
+ onProxyChange(fn: (state: ProxyState, errorText: string) => void) {
+ this.removeOnProxyChange(fn);
+ this.stateChangeListeners.push(fn);
+ }
+
+ removeOnProxyChange(removeFn: (state: ProxyState, errorText: string) => void) {
+ this.stateChangeListeners = this.stateChangeListeners.filter((fn) => fn !== removeFn);
+ }
+
+ getDevices() {
+ if (this.state !== ProxyState.DEVICES && this.state !== ProxyState.CONNECTING) {
+ clearInterval(this.refresh_worker!);
+ this.refresh_worker = null;
+ return;
+ }
+ proxyRequest.getDevices(this);
+ }
+
+ selectDevice(device_id: string) {
+ this.selectedDevice = device_id;
+ this.store.add('adb.lastDevice', device_id);
+ this.setState(ProxyState.START_TRACE);
+ }
+
+ async updateAdbData(files: string[], traceType: string, progressCallback: OnProgressUpdateType) {
+ for (let idx = 0; idx < files.length; idx++) {
+ const adbParams = {
+ files,
+ idx,
+ traceType,
+ };
+ await proxyRequest.fetchFiles(this.selectedDevice, adbParams);
+ progressCallback((100 * (idx + 1)) / files.length);
+ }
+ }
+}
+
+export const proxyClient = new ProxyClient();
diff --git a/tools/winscope/src/trace_collection/proxy_connection.ts b/tools/winscope/src/trace_collection/proxy_connection.ts
new file mode 100644
index 0000000..231e0a5
--- /dev/null
+++ b/tools/winscope/src/trace_collection/proxy_connection.ts
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2022, 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 {FunctionUtils, OnProgressUpdateType} from 'common/function_utils';
+import {proxyClient, ProxyEndpoint, proxyRequest, ProxyState} from 'trace_collection/proxy_client';
+import {Connection, DeviceProperties} from './connection';
+import {ConfigMap} from './trace_collection_utils';
+import {TracingConfig} from './tracing_config';
+
+export class ProxyConnection implements Connection {
+ proxy = proxyClient;
+ keep_alive_worker: any = null;
+ notConnected = [ProxyState.NO_PROXY, ProxyState.UNAUTH, ProxyState.INVALID_VERSION];
+
+ constructor(
+ private proxyStateChangeCallback: (state: ProxyState) => void,
+ private progressCallback: OnProgressUpdateType = FunctionUtils.DO_NOTHING
+ ) {
+ this.proxy.setState(ProxyState.CONNECTING);
+ this.proxy.onProxyChange((newState) => this.onConnectChange(newState));
+ const urlParams = new URLSearchParams(window.location.search);
+ if (urlParams.has('token')) {
+ this.proxy.proxyKey = urlParams.get('token')!;
+ } else if (this.proxy.store.get('adb.proxyKey')) {
+ this.proxy.proxyKey = this.proxy.store.get('adb.proxyKey')!;
+ }
+ this.proxy.getDevices();
+ }
+
+ devices() {
+ return this.proxy.devices;
+ }
+
+ adbData() {
+ return this.proxy.adbData;
+ }
+
+ state() {
+ return this.proxy.state;
+ }
+
+ isDevicesState() {
+ return this.state() === ProxyState.DEVICES;
+ }
+
+ isStartTraceState() {
+ return this.state() === ProxyState.START_TRACE;
+ }
+
+ isErrorState() {
+ return this.state() === ProxyState.ERROR;
+ }
+
+ isEndTraceState() {
+ return this.state() === ProxyState.END_TRACE;
+ }
+
+ isLoadDataState() {
+ return this.state() === ProxyState.LOAD_DATA;
+ }
+
+ isConnectingState() {
+ return this.state() === ProxyState.CONNECTING;
+ }
+
+ throwNoTargetsError() {
+ this.proxy.setState(ProxyState.ERROR, 'No targets selected');
+ }
+
+ setProxyKey(key: string) {
+ this.proxy.proxyKey = key;
+ this.proxy.store.add('adb.proxyKey', key);
+ this.restart();
+ }
+
+ adbSuccess() {
+ return !this.notConnected.includes(this.proxy.state);
+ }
+
+ selectedDevice(): DeviceProperties {
+ return this.proxy.devices[this.proxy.selectedDevice];
+ }
+
+ selectedDeviceId(): string {
+ return this.proxy.selectedDevice;
+ }
+
+ restart() {
+ this.proxy.setState(ProxyState.CONNECTING);
+ }
+
+ resetLastDevice() {
+ this.proxy.store.add('adb.lastDevice', '');
+ this.restart();
+ }
+
+ selectDevice(id: string) {
+ this.proxy.selectDevice(id);
+ }
+
+ keepAliveTrace(view: ProxyConnection) {
+ if (!view.isEndTraceState()) {
+ clearInterval(view.keep_alive_worker);
+ view.keep_alive_worker = null;
+ return;
+ }
+ proxyRequest.keepTraceAlive(view);
+ }
+
+ startTrace(
+ reqEnableConfig?: string[],
+ reqSelectedSfConfig?: ConfigMap,
+ reqSelectedWmConfig?: ConfigMap
+ ) {
+ if (reqEnableConfig) {
+ proxyRequest.setEnabledConfig(this, reqEnableConfig);
+ }
+ if (reqSelectedSfConfig) {
+ proxyRequest.setSelectedConfig(
+ ProxyEndpoint.SELECTED_SF_CONFIG_TRACE,
+ this,
+ reqSelectedSfConfig
+ );
+ }
+ if (reqSelectedWmConfig) {
+ proxyRequest.setSelectedConfig(
+ ProxyEndpoint.SELECTED_WM_CONFIG_TRACE,
+ this,
+ reqSelectedWmConfig
+ );
+ }
+ proxyClient.setState(ProxyState.END_TRACE);
+ proxyRequest.startTrace(this, TracingConfig.getInstance().requestedTraces);
+ }
+
+ async endTrace() {
+ this.progressCallback(0);
+ this.proxy.setState(ProxyState.LOAD_DATA);
+ await proxyRequest.endTrace(this, this.progressCallback);
+ }
+
+ async dumpState(): Promise<boolean> {
+ this.progressCallback(0);
+ if (TracingConfig.getInstance().requestedDumps.length < 1) {
+ console.error('No targets selected');
+ this.proxy.setState(ProxyState.ERROR, 'No targets selected');
+ return false;
+ }
+ this.proxy.setState(ProxyState.LOAD_DATA);
+ await proxyRequest.dumpState(
+ this,
+ TracingConfig.getInstance().requestedDumps,
+ this.progressCallback
+ );
+ return true;
+ }
+
+ async isWaylandAvailable(): Promise<boolean> {
+ return new Promise((resolve, reject) => {
+ proxyRequest.call('GET', ProxyEndpoint.CHECK_WAYLAND, (request: XMLHttpRequest) => {
+ resolve(request.responseText === 'true');
+ });
+ });
+ }
+
+ async onConnectChange(newState: ProxyState) {
+ if (newState === ProxyState.CONNECTING) {
+ proxyClient.getDevices();
+ }
+ if (newState === ProxyState.START_TRACE) {
+ const isWaylandAvailable = await this.isWaylandAvailable();
+ TracingConfig.getInstance().setTraceConfigForAvailableTraces(isWaylandAvailable);
+ }
+ this.proxyStateChangeCallback(newState);
+ }
+}
diff --git a/tools/winscope/src/trace_collection/trace_collection_utils.ts b/tools/winscope/src/trace_collection/trace_collection_utils.ts
new file mode 100644
index 0000000..5654616
--- /dev/null
+++ b/tools/winscope/src/trace_collection/trace_collection_utils.ts
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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 interface TraceConfiguration {
+ name: string | undefined;
+ run: boolean | undefined;
+ isTraceCollection: boolean | undefined;
+ config: ConfigurationOptions | undefined;
+}
+
+export interface TraceConfigurationMap {
+ [key: string]: TraceConfiguration;
+}
+
+interface ConfigurationOptions {
+ enableConfigs: EnableConfiguration[];
+ selectionConfigs: SelectionConfiguration[];
+}
+
+export interface EnableConfiguration {
+ name: string;
+ key: string;
+ enabled: boolean;
+}
+
+export interface SelectionConfiguration {
+ key: string;
+ name: string;
+ options: string[];
+ value: string;
+}
+
+export interface ConfigMap {
+ [key: string]: string[] | string;
+}
+
+const wmTraceSelectionConfigs: SelectionConfiguration[] = [
+ {
+ key: 'wmbuffersize',
+ name: 'buffer size (KB)',
+ options: ['4000', '8000', '16000', '32000'],
+ value: '32000',
+ },
+ {
+ key: 'tracingtype',
+ name: 'tracing type',
+ options: ['frame', 'transaction'],
+ value: 'frame',
+ },
+ {
+ key: 'tracinglevel',
+ name: 'tracing level',
+ options: ['verbose', 'debug', 'critical'],
+ value: 'verbose',
+ },
+];
+
+const sfTraceEnableConfigs: EnableConfiguration[] = [
+ {
+ name: 'input',
+ key: 'input',
+ enabled: true,
+ },
+ {
+ name: 'composition',
+ key: 'composition',
+ enabled: true,
+ },
+ {
+ name: 'metadata',
+ key: 'metadata',
+ enabled: false,
+ },
+ {
+ name: 'hwc',
+ key: 'hwc',
+ enabled: true,
+ },
+ {
+ name: 'trace buffers',
+ key: 'tracebuffers',
+ enabled: true,
+ },
+ {
+ name: 'virtual displays',
+ key: 'virtualdisplays',
+ enabled: false,
+ },
+];
+
+const sfTraceSelectionConfigs: SelectionConfiguration[] = [
+ {
+ key: 'sfbuffersize',
+ name: 'buffer size (KB)',
+ options: ['4000', '8000', '16000', '32000'],
+ value: '32000',
+ },
+];
+
+export const traceConfigurations: TraceConfigurationMap = {
+ layers_trace: {
+ name: 'Surface Flinger',
+ run: true,
+ isTraceCollection: undefined,
+ config: {
+ enableConfigs: sfTraceEnableConfigs,
+ selectionConfigs: sfTraceSelectionConfigs,
+ },
+ },
+ window_trace: {
+ name: 'Window Manager',
+ run: true,
+ isTraceCollection: undefined,
+ config: {
+ enableConfigs: [],
+ selectionConfigs: wmTraceSelectionConfigs,
+ },
+ },
+ screen_recording: {
+ name: 'Screen Recording',
+ isTraceCollection: undefined,
+ run: true,
+ config: undefined,
+ },
+ ime_tracing: {
+ name: 'IME Tracing',
+ run: true,
+ isTraceCollection: true,
+ config: {
+ enableConfigs: [
+ {
+ name: 'Input Method Clients',
+ key: 'ime_trace_clients',
+ enabled: true,
+ },
+ {
+ name: 'Input Method Service',
+ key: 'ime_trace_service',
+ enabled: true,
+ },
+ {
+ name: 'Input Method Manager Service',
+ key: 'ime_trace_managerservice',
+ enabled: true,
+ },
+ ],
+ selectionConfigs: [],
+ },
+ },
+ ime_trace_clients: {
+ name: 'Input Method Clients',
+ isTraceCollection: undefined,
+ run: true,
+ config: undefined,
+ },
+ ime_trace_service: {
+ name: 'Input Method Service',
+ isTraceCollection: undefined,
+ run: true,
+ config: undefined,
+ },
+ ime_trace_managerservice: {
+ name: 'Input Method Manager Service',
+ isTraceCollection: undefined,
+ run: true,
+ config: undefined,
+ },
+ accessibility_trace: {
+ name: 'Accessibility',
+ isTraceCollection: undefined,
+ run: false,
+ config: undefined,
+ },
+ transactions: {
+ name: 'Transaction',
+ isTraceCollection: undefined,
+ run: false,
+ config: undefined,
+ },
+ proto_log: {
+ name: 'ProtoLog',
+ isTraceCollection: undefined,
+ run: false,
+ config: undefined,
+ },
+ wayland_trace: {
+ name: 'Wayland',
+ isTraceCollection: undefined,
+ run: false,
+ config: undefined,
+ },
+ eventlog: {
+ name: 'Event Log',
+ isTraceCollection: undefined,
+ run: false,
+ config: undefined,
+ },
+ transition_traces: {
+ name: 'Shell Transitions',
+ isTraceCollection: undefined,
+ run: false,
+ config: undefined,
+ },
+};
+
+export const TRACES: {[key: string]: TraceConfigurationMap} = {
+ default: {
+ window_trace: traceConfigurations['window_trace'],
+ accessibility_trace: traceConfigurations['accessibility_trace'],
+ layers_trace: traceConfigurations['layers_trace'],
+ transactions: traceConfigurations['transactions'],
+ proto_log: traceConfigurations['proto_log'],
+ screen_recording: traceConfigurations['screen_recording'],
+ ime_tracing: traceConfigurations['ime_tracing'],
+ eventlog: traceConfigurations['eventlog'],
+ transition_traces: traceConfigurations['transition_traces'],
+ },
+ arc: {
+ wayland_trace: traceConfigurations['wayland_trace'],
+ },
+};
diff --git a/tools/winscope/src/trace_collection/tracing_config.ts b/tools/winscope/src/trace_collection/tracing_config.ts
new file mode 100644
index 0000000..ddee901
--- /dev/null
+++ b/tools/winscope/src/trace_collection/tracing_config.ts
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022, 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 {globalConfig} from 'common/global_config';
+import {PersistentStoreProxy} from 'common/persistent_store_proxy';
+import {MockStorage} from 'test/unit/mock_storage';
+import {TraceConfigurationMap, TRACES} from './trace_collection_utils';
+
+export class TracingConfig {
+ requestedTraces: string[] = [];
+ requestedDumps: string[] = [];
+
+ private storage: Storage;
+ private traceConfig: TraceConfigurationMap;
+ private dumpConfig: TraceConfigurationMap;
+
+ private static instance: TracingConfig | undefined;
+
+ static getInstance(): TracingConfig {
+ if (!TracingConfig.instance) {
+ TracingConfig.instance = new TracingConfig();
+ }
+ return TracingConfig.instance;
+ }
+
+ setTraceConfigForAvailableTraces(isWaylandAvailable = false) {
+ const availableTracesConfig = TRACES['default'];
+ if (isWaylandAvailable) {
+ Object.assign(availableTracesConfig, TRACES['arc']);
+ }
+ this.setTraceConfig(availableTracesConfig);
+ }
+
+ setTraceConfig(traceConfig: TraceConfigurationMap) {
+ this.traceConfig = PersistentStoreProxy.new<TraceConfigurationMap>(
+ 'TraceConfiguration',
+ traceConfig,
+ this.storage
+ );
+ }
+
+ getTraceConfig(): TraceConfigurationMap {
+ return this.traceConfig;
+ }
+
+ getDumpConfig(): TraceConfigurationMap {
+ if (this.dumpConfig === undefined) {
+ throw Error('Dump config not initialized yet');
+ }
+ return this.dumpConfig;
+ }
+
+ private constructor() {
+ this.storage = globalConfig.MODE === 'PROD' ? localStorage : new MockStorage();
+
+ this.traceConfig = PersistentStoreProxy.new<TraceConfigurationMap>(
+ 'TracingSettings',
+ TRACES['default'],
+ this.storage
+ );
+
+ this.dumpConfig = PersistentStoreProxy.new<TraceConfigurationMap>(
+ 'DumpSettings',
+ {
+ window_dump: {
+ name: 'Window Manager',
+ isTraceCollection: undefined,
+ run: true,
+ config: undefined,
+ },
+ layers_dump: {
+ name: 'Surface Flinger',
+ isTraceCollection: undefined,
+ run: true,
+ config: undefined,
+ },
+ },
+ this.storage
+ );
+ }
+}
diff --git a/tools/winscope/src/traces/Accessibility.ts b/tools/winscope/src/traces/Accessibility.ts
deleted file mode 100644
index 523c571..0000000
--- a/tools/winscope/src/traces/Accessibility.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 Accessibility extends TraceBase {
- accessibilityTraceFile: Object;
-
- constructor(files) {
- const accessibilityTraceFile = files[FILE_TYPES.ACCESSIBILITY_TRACE];
- super(accessibilityTraceFile.data, accessibilityTraceFile.timeline, files);
-
- this.accessibilityTraceFile = accessibilityTraceFile;
- }
-
- get type() {
- return TRACE_TYPES.ACCESSIBILITY;
- }
-}
diff --git a/tools/winscope/src/traces/InputMethodClients.ts b/tools/winscope/src/traces/InputMethodClients.ts
deleted file mode 100644
index 95e1d8e..0000000
--- a/tools/winscope/src/traces/InputMethodClients.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 7ae0434..0000000
--- a/tools/winscope/src/traces/InputMethodManagerService.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 64b7841..0000000
--- a/tools/winscope/src/traces/InputMethodService.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 9649296..0000000
--- a/tools/winscope/src/traces/Launcher.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index f251114..0000000
--- a/tools/winscope/src/traces/ProtoLog.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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
deleted file mode 100644
index c781d7d..0000000
--- a/tools/winscope/src/traces/ScreenRecording.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b8ec6df..0000000
--- a/tools/winscope/src/traces/SurfaceFlinger.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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 { LayersTrace } from '@/flickerlib';
-
-export default class SurfaceFlinger extends TraceBase {
- readonly sfTraceFile: Object;
- readonly tagGenerationTrace: Object;
-
- constructor(files) {
- const sfTraceFile = Object.freeze(files[FILE_TYPES.SURFACE_FLINGER_TRACE]);
- const tagGenerationTrace = files[FILE_TYPES.SURFACE_FLINGER_TRACE].tagGenerationTrace;
- super(sfTraceFile.data, sfTraceFile.timeline, files);
-
- this.tagGenerationTrace = Object.freeze(tagGenerationTrace);
- this.sfTraceFile = sfTraceFile;
- }
-
- get type() {
- return TRACE_TYPES.SURFACE_FLINGER;
- }
-
- static fromProto(proto: any): LayersTrace {
- return LayersTrace.fromProto(proto);
- }
-}
diff --git a/tools/winscope/src/traces/SystemUI.ts b/tools/winscope/src/traces/SystemUI.ts
deleted file mode 100644
index 4bc28d4..0000000
--- a/tools/winscope/src/traces/SystemUI.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index ab9666c..0000000
--- a/tools/winscope/src/traces/TraceBase.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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;
- readonly data: Object;
- readonly timeline: Array<Number>;
- readonly _files: File[];
-
- constructor(data: any, timeline: Number[], files: any[]) {
- this.selectedIndex = 0;
- this.data = data;
- this.timeline = timeline;
- this._files = files;
- }
-
- get files(): readonly 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: readonly Object[];
- type: String,
-}
diff --git a/tools/winscope/src/traces/TraceError.ts b/tools/winscope/src/traces/TraceError.ts
deleted file mode 100644
index 3c9daa7..0000000
--- a/tools/winscope/src/traces/TraceError.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 { ErrorTrace } from '@/flickerlib';
-
-export default class TraceError extends TraceBase {
- errorTraceFile: Object;
-
- constructor(files) {
- const errorTraceFile = files[FILE_TYPES.ERROR_TRACE];
- super(errorTraceFile.data, errorTraceFile.timeline, files);
- this.errorTraceFile = errorTraceFile;
- }
-
- get type() {
- return TRACE_TYPES.ERROR;
- }
-
- static fromProto(proto: any): ErrorTrace {
- return ErrorTrace.fromProto(proto);
- }
-}
diff --git a/tools/winscope/src/traces/TraceTag.ts b/tools/winscope/src/traces/TraceTag.ts
deleted file mode 100644
index 401a185..0000000
--- a/tools/winscope/src/traces/TraceTag.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 { TagTrace } from '@/flickerlib';
-
-export default class TraceTag extends TraceBase {
- tagTraceFile: Object;
-
- constructor(files) {
- const tagTraceFile = files[FILE_TYPES.TAG_TRACE];
- super(tagTraceFile.data, tagTraceFile.timeline, files);
- this.tagTraceFile = tagTraceFile;
- }
-
- get type() {
- return TRACE_TYPES.TAG;
- }
-
- static fromProto(proto: any): TagTrace {
- return TagTrace.fromProto(proto);
- }
-}
\ No newline at end of file
diff --git a/tools/winscope/src/traces/Transactions.ts b/tools/winscope/src/traces/Transactions.ts
deleted file mode 100644
index 587d76b..0000000
--- a/tools/winscope/src/traces/Transactions.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2021, 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 TransactionsTrace extends TraceBase {
- transactionsFile: Object;
-
- 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;
- }
-}
diff --git a/tools/winscope/src/traces/TransactionsLegacy.ts b/tools/winscope/src/traces/TransactionsLegacy.ts
deleted file mode 100644
index ad25d42..0000000
--- a/tools/winscope/src/traces/TransactionsLegacy.ts
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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';
-
-/**
- * @deprecated This trace has been replaced by the new transactions trace
- */
-export default class TransactionsTraceLegacy extends TraceBase {
- transactionsFile: Object;
- transactionHistory: TransactionHistory;
-
- constructor(files: any[]) {
- const transactionsFile = files[FILE_TYPES.TRANSACTIONS_TRACE_LEGACY];
-
- super(transactionsFile.data, transactionsFile.timeline, files);
-
- this.transactionsFile = transactionsFile;
-
- // Create new transaction history
- this.transactionHistory = new TransactionHistory(transactionsFile);
- }
-
- get type() {
- return TRACE_TYPES.TRANSACTION_LEGACY;
- }
-}
-
-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
deleted file mode 100644
index b5924d8..0000000
--- a/tools/winscope/src/traces/Wayland.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
deleted file mode 100644
index eb41ad9..0000000
--- a/tools/winscope/src/traces/WindowManager.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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;
- tagGenerationTrace: Object;
-
- constructor(files) {
- const wmTraceFile = files[FILE_TYPES.WINDOW_MANAGER_TRACE];
- const tagGenerationTrace = files[FILE_TYPES.WINDOW_MANAGER_TRACE].tagGenerationTrace;
- super(wmTraceFile.data, wmTraceFile.timeline, files);
-
- this.tagGenerationTrace = tagGenerationTrace;
- this.wmTraceFile = wmTraceFile;
- }
-
- get type() {
- return TRACE_TYPES.WINDOW_MANAGER;
- }
-
- static fromProto(proto: any): WindowManagerTrace {
- return WindowManagerTrace.fromProto(proto);
- }
-}
diff --git a/tools/winscope/src/transform.js b/tools/winscope/src/transform.js
deleted file mode 100644
index 51bedaf..0000000
--- a/tools/winscope/src/transform.js
+++ /dev/null
@@ -1,418 +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 {DiffType} from './utils/diff.js';
-import {regExpTimestampSearch} from './utils/consts';
-
-// 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].
-// 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,
- 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;
- }
- }
-
- const transformedChildren = handleChildren(children, true /* transform */);
- rectsTransform = (rectsTransform === undefined) ? (e) => e : rectsTransform;
-
- 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);
-
- 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.isVisible;
- }),
- flattened: call(flattened, obj),
- };
-
- if (rectResolved) {
- rectResolved.ref = result;
- }
-
- return freeze ? Object.freeze(result) : result;
-}
-
-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: (isTerminal(name) ? compareWithName : 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;
- }
- }
- }
-
- if (transformOptions.keepOriginal) {
- transformedObj.obj = originalObj;
- }
-
- if (metadata) {
- transformedObj[transformOptions.metadataKey] = metadata;
- }
-
- return transformOptions.freeze ?
- Object.freeze(transformedObj) : transformedObj;
- }
-}
-
-// eslint-disable-next-line camelcase
-function nanos_to_string(elapsedRealtimeNanos) {
- const units = [
- [1000000, '(ns)'],
- [1000, 'ms'],
- [60, 's'],
- [60, 'm'],
- [24, 'h'],
- [Infinity, 'd'],
- ];
-
- 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('');
-}
-
-function string_to_nanos(stringTime) {
- //isolate the times for each unit in an array
- var times = stringTime.split(/\D+/).filter(unit => unit.length > 0);
-
- //add zeroes to start of array if only partial timestamp is input
- while (times.length<5) {
- times.unshift("0");
- }
-
- var units = [24*60*60, 60*60, 60, 1, 0.001];
- var nanos = 0;
- //multiply the times by the relevant unit and sum
- for (var x=0; x<5; x++) {
- nanos += units[x]*parseInt(times[x]);
- }
- return nanos*(10**9);
-}
-
-// 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'};
-}
-
-// Returns closest timestamp in timeline based on search input*/
-function getClosestTimestamp(searchInput, timeline) {
- if (regExpTimestampSearch.test(searchInput)) {
- var roundedTimestamp = parseInt(searchInput);
- } else {
- var roundedTimestamp = string_to_nanos(searchInput);
- }
- const closestTimestamp = timeline.reduce((prev, curr) => {
- return Math.abs(curr-roundedTimestamp) < Math.abs(prev-roundedTimestamp) ? curr : prev;
- });
- return closestTimestamp;
-}
-
-// eslint-disable-next-line camelcase
-export {transform, ObjectTransformer, nanos_to_string, string_to_nanos, get_visible_chip, getClosestTimestamp};
diff --git a/tools/winscope/src/transform_accessibility.js b/tools/winscope/src/transform_accessibility.js
deleted file mode 100644
index 6c622f2..0000000
--- a/tools/winscope/src/transform_accessibility.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 { transform, nanos_to_string, get_visible_chip } from './transform.js'
-
-function transform_accessibility(accessibility) {
- return transform({
- obj: accessibility,
- kind: 'accessibility',
- name: 'accessibility',
- children: []
- });
-}
-
-function transform_entry(entry) {
- return transform({
- obj: entry,
- kind: 'entry',
- name: nanos_to_string(entry.elapsedRealtimeNanos),
- children: [
- [entry.accessibilityService, transform_accessibility],
- ],
- timestamp: entry.elapsedRealtimeNanos,
- stableId: 'entry'
- });
-}
-
-function transform_accessibility_trace(entries) {
- return transform({
- obj: entries,
- kind: 'entries',
- name: 'entries',
- children: [
- [entries.entry, transform_entry],
- ],
- });
-}
-
-export { transform_accessibility_trace };
diff --git a/tools/winscope/src/transform_ime.js b/tools/winscope/src/transform_ime.js
deleted file mode 100644
index cd317c8..0000000
--- a/tools/winscope/src/transform_ime.js
+++ /dev/null
@@ -1,123 +0,0 @@
-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
deleted file mode 100644
index 24a1376..0000000
--- a/tools/winscope/src/transform_launcher.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 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.
- */
-
-import { transform, nanos_to_string, get_visible_chip } from './transform.js'
-
-function transform_launcher(launcher) {
- return transform({
- obj: launcher,
- kind: 'launcher',
- name: 'launcher',
- children: []
- });
-}
-
-function transform_entry(entry) {
- return transform({
- obj: entry,
- kind: 'entry',
- name: nanos_to_string(entry.elapsedRealtimeNanos),
- children: [
- [[entry.launcher], transform_launcher]
- ],
- timestamp: entry.elapsedRealtimeNanos,
- stableId: 'entry'
- });
-}
-
-function transform_launcher_trace(entries) {
- return transform({
- obj: entries,
- kind: 'entries',
- name: 'entries',
- children: [
- [entries.entry, transform_entry],
- ],
- });
-}
-
-export { transform_launcher_trace };
diff --git a/tools/winscope/src/transform_protolog.js b/tools/winscope/src/transform_protolog.js
deleted file mode 100644
index c3f9122..0000000
--- a/tools/winscope/src/transform_protolog.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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 viewerConfig
- from '../../../../frameworks/base/data/etc/services.core.protolog.json';
-
-import {FormattedLogMessage, UnformattedLogMessage} from '@/traces/ProtoLog.ts';
-
-const PROTOLOG_VERSION = '1.0.0';
-
-class FormatStringMismatchError extends Error {
- constructor(message) {
- super(message);
- }
-}
-
-function transformMessage(entry) {
- const message = viewerConfig.messages[entry.messageHash];
- if (message === undefined) {
- return new FormattedLogMessage(entry);
- } else {
- try {
- return new UnformattedLogMessage(entry, message);
- } catch (err) {
- if (err instanceof FormatStringMismatchError) {
- return new FormattedLogMessage(entry);
- }
- throw err;
- }
- }
-}
-
-function transformProtolog(log) {
- if (log.version !== PROTOLOG_VERSION) {
- throw new Error('Unsupported log version');
- }
- if (viewerConfig.version !== PROTOLOG_VERSION) {
- throw new Error('Unsupported viewer config version');
- }
-
- 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 {transformProtolog};
diff --git a/tools/winscope/src/transform_sys_ui.js b/tools/winscope/src/transform_sys_ui.js
deleted file mode 100644
index 3b14546..0000000
--- a/tools/winscope/src/transform_sys_ui.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 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.
- */
-
-import { transform, nanos_to_string, get_visible_chip } from './transform.js'
-
-function transform_edgeBack(edgeBack) {
- return transform({
- obj: edgeBack,
- kind: 'edgeBack',
- name: 'edgeBack',
- children: []
- });
-}
-
-function transform_systemUi(sysui) {
- return transform({
- obj: sysui,
- kind: 'systemUi',
- name: 'systemUi',
- children: [
- [[sysui.edgeBackGestureHandler], transform_edgeBack]
- ]
- });
-}
-
-function transform_entry(entry) {
- return transform({
- obj: entry,
- kind: 'entry',
- name: nanos_to_string(entry.elapsedRealtimeNanos),
- children: [
- [[entry.systemUi], transform_systemUi]
- ],
- timestamp: entry.elapsedRealtimeNanos,
- stableId: 'entry'
- });
-}
-
-function transform_sysui_trace(entries) {
- return transform({
- obj: entries,
- kind: 'entries',
- name: 'entries',
- children: [
- [entries.entry, transform_entry],
- ],
- });
-}
-
-export { transform_sysui_trace };
diff --git a/tools/winscope/src/transform_transaction.js b/tools/winscope/src/transform_transaction.js
deleted file mode 100644
index baf816a..0000000
--- a/tools/winscope/src/transform_transaction.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2021, 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 {nanos_to_string, transform} from './transform.js'
-
-function transform_change(change_data) {
- const kind = change_data.__proto__.$type.name;
- const name = change_data.layerId || change_data.id;
- return transform({
- kind: kind,
- name: name,
- stableId: kind + name,
- obj: change_data,
- children: [],
- isVisible: true,
- });
-}
-
-function transform_transaction_state(transaction_state) {
- const obj = Object.assign({}, transaction_state)
- if (obj.displayChanges) delete obj.displayChanges;
- if (obj.layerChanges) delete obj.layerChanges;
- const stableId = 'pid=' + transaction_state.pid +
- ' uid=' + transaction_state.uid +
- ' postTime=' + transaction_state.postTime;
- return transform({
- kind: 'TransactionState',
- name: stableId,
- stableId: stableId,
- obj: obj,
- children: [
- [
- [...transaction_state.layerChanges, ...transaction_state.displayChanges],
- transform_change]
- ]
- });
-}
-
-function transform_transaction_trace_entry(entry) {
- const obj = Object.assign({}, entry)
- if (obj.transactions) delete obj.transactions;
-
- return transform({
- obj: obj,
- kind: 'entry',
- stableId: 'entry',
- timestamp: entry.elapsedRealtimeNanos,
- name: nanos_to_string(entry.elapsedRealtimeNanos),
- children: [
- [entry.transactions, transform_transaction_state]
- ],
- });
-}
-
-function transform_transaction_trace(trace) {
- const data = trace.entry.map((entry) => transform_transaction_trace_entry(entry));
- return {children: data};
-}
-
-export {transform_transaction_trace};
diff --git a/tools/winscope/src/transform_transaction_legacy.js b/tools/winscope/src/transform_transaction_legacy.js
deleted file mode 100644
index bfd2795..0000000
--- a/tools/winscope/src/transform_transaction_legacy.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 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.
- */
-
-import {nanos_to_string} from './transform.js';
-
-function transform_transaction(transaction, layerIdToName) {
- const transactions = [];
-
- for (const surfaceChange of transaction.surfaceChange) {
- transactions.push(Object.freeze({
- type: 'surfaceChange',
- obj: surfaceChange,
- layerName: layerIdToName[surfaceChange.id],
- }));
- }
-
- for (const displayChange of transaction.displayChange) {
- transactions.push(Object.freeze({
- type: 'displayChange',
- obj: displayChange,
- layerName: layerIdToName[displayChange.id],
- }));
- }
-
- return transactions;
-}
-
-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,
- });
- }
-}
-
-/**
- * @deprecated This trace has been replaced by the new transactions trace
- */
-function transform_transaction_trace_legacy(entries) {
- const layerIdToName = {};
- const data = entries.increment.map((entry) => transform_entry(entry, layerIdToName));
-
- return {children: data};
-}
-
-export {transform_transaction_trace_legacy};
diff --git a/tools/winscope/src/transform_wl.js b/tools/winscope/src/transform_wl.js
deleted file mode 100644
index aafc637..0000000
--- a/tools/winscope/src/transform_wl.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 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.
- */
-
-import {transform, nanos_to_string, get_visible_chip} from './transform.js'
-
-function transform_wl_layer(layer) {
- function is_visible(layer) {
- return layer.parent == 0 || (layer.visibleInParent && layer.visible && (layer.hidden != 1));
- }
-
- var chips = [];
- var rect = layer.displayFrame;
- var visible = is_visible(layer);
- if (visible && layer.parent != 0) {
- chips.push(get_visible_chip());
- }
- if (!visible) {
- rect = undefined;
- }
-
- return transform({
- obj: layer,
- kind: 'layer',
- name: layer.name,
- children: [
- [layer.resolvedChildren, transform_wl_layer],
- ],
- rect,
- highlight: rect,
- chips,
- visible,
- stableId: layer.id,
- });
-}
-
-function transform_wl_container(cntnr) {
- var rect = cntnr.geometry;
- var layersList = cntnr.layers || [];
-
- return transform({
- obj: cntnr,
- kind: 'container',
- name: cntnr.name,
- children: [
- [layersList, transform_wl_layer],
- ],
- rect,
- highlight: rect,
- stableId: cntnr.id,
- });
-}
-
-function transform_wl_outputstate(layers) {
- var containerList = layers.containers || [];
- var fullBounds = layers.fullBounds;
-
- return transform({
- obj: {name: "Output State", fullBounds: fullBounds},
- kind: 'outputstate',
- name: 'Output State',
- rect: fullBounds,
- highlight: fullBounds,
- children: [
- [containerList, transform_wl_container],
- ],
- });
-}
-
-function transform_wl_entry(entry) {
- return transform({
- obj: entry,
- kind: 'entry',
- name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
- children: [
- [[entry.state], transform_wl_outputstate],
- ],
- timestamp: entry.elapsedRealtimeNanos,
- stableId: 'entry',
- });
-}
-
-function transform_wayland_trace(entries) {
- var r = transform({
- obj: entries,
- kind: 'wltrace',
- name: 'wltrace',
- children: [
- [entries.entry, transform_wl_entry],
- ],
- });
-
- return r;
-}
-
-export {transform_wl_outputstate, transform_wayland_trace};
diff --git a/tools/winscope/src/utils/compatibility.js b/tools/winscope/src/utils/compatibility.js
deleted file mode 100644
index 5685f37..0000000
--- a/tools/winscope/src/utils/compatibility.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 4be6a44..0000000
--- a/tools/winscope/src/utils/consts.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 TransitionType from "../flickerlib/tags/TransitionType";
-
-/**
- * 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 SEARCH_TYPE = {
- TRANSITIONS: 'Transitions',
- ERRORS: 'Errors',
- TIMESTAMP: 'Timestamp',
-};
-
-const logLevel = {
- INFO: 'info',
- DEBUG: 'debug',
- VERBOSE: 'verbose',
- WARN: 'warn',
- ERROR: 'error',
- WTF: 'wtf',
-};
-
-const transitionMap = new Map([
- [TransitionType.ROTATION, {desc: 'Rotation', color: '#9900ffff'}],
- [TransitionType.PIP_ENTER, {desc: 'Entering PIP mode', color: '#4a86e8ff'}],
- [TransitionType.PIP_RESIZE, {desc: 'Resizing PIP mode', color: '#2b9e94ff'}],
- [TransitionType.PIP_EXPAND, {desc: 'Expanding PIP mode', color: 'rgb(57, 57, 182)'}],
- [TransitionType.PIP_EXIT, {desc: 'Exiting PIP mode', color: 'darkblue'}],
- [TransitionType.APP_LAUNCH, {desc: 'Launching app', color: '#ef6befff'}],
- [TransitionType.APP_CLOSE, {desc: 'Closing app', color: '#d10ddfff'}],
- [TransitionType.IME_APPEAR, {desc: 'IME appearing', color: '#ff9900ff'}],
- [TransitionType.IME_DISAPPEAR, {desc: 'IME disappearing', color: '#ad6800ff'}],
- [TransitionType.APP_PAIRS_ENTER, {desc: 'Entering app pairs mode', color: 'rgb(58, 151, 39)'}],
- [TransitionType.APP_PAIRS_EXIT, {desc: 'Exiting app pairs mode', color: 'rgb(45, 110, 32)'}],
-])
-
-//used to split timestamp search input by unit, to convert to nanoseconds
-const regExpTimestampSearch = new RegExp(/^\d+$/);
-
-export { WebContentScriptMessageType, NAVIGATION_STYLE, SEARCH_TYPE, logLevel, transitionMap, regExpTimestampSearch };
diff --git a/tools/winscope/src/utils/diff.js b/tools/winscope/src/utils/diff.js
deleted file mode 100644
index 76d70e5..0000000
--- a/tools/winscope/src/utils/diff.js
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * 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 ObjectFormatter from '../flickerlib/ObjectFormatter';
-
-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 !newNode.equals(oldNode);
-}
-
-export class DiffGenerator {
- constructor(tree) {
- this.tree = tree;
- }
-
- compareWith(tree) {
- 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;
- }
-
- _cloneNode(node) {
- const clone = ObjectFormatter.cloneObject(node);
- clone.children = node.children;
- clone.name = node.name;
- clone.kind = node.kind;
- clone.stableId = node.stableId;
- clone.shortName = node.shortName;
- 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._cloneNode(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._cloneNode(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._cloneNode(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
deleted file mode 100644
index a5ae037..0000000
--- a/tools/winscope/src/utils/names.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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
deleted file mode 100644
index fae19f3..0000000
--- a/tools/winscope/src/utils/utils.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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('');
-}
-
-/** Checks for match in window manager properties taskId, layerId, or windowToken,
- * or surface flinger property id
- */
-function isPropertyMatch(flickerItem, entryItem) {
- return flickerItem.taskId === entryItem.taskId ||
- (flickerItem.windowToken === entryItem.windowToken) ||
- ((flickerItem.layerId === entryItem.layerId) && flickerItem.layerId !== 0) ||
- flickerItem.layerId === entryItem.id;
-}
-
-export {
- DIRECTION,
- findLastMatchingSorted,
- isPropertyMatch,
- stableIdCompatibilityFixup,
- nanosToString,
- TimeUnits
-}
\ No newline at end of file
diff --git a/tools/winscope/src/viewers/common/chip.ts b/tools/winscope/src/viewers/common/chip.ts
new file mode 100644
index 0000000..cefb8dc
--- /dev/null
+++ b/tools/winscope/src/viewers/common/chip.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.
+ */
+
+export class Chip {
+ short: string;
+ long: string;
+ type: string;
+
+ constructor(short: string, long: string, type: string) {
+ this.short = short;
+ this.long = long;
+ this.type = type;
+ }
+}
+
+export const VISIBLE_CHIP = new Chip('V', 'visible', 'default');
+
+export const RELATIVE_Z_CHIP = new Chip('RelZ', 'Is relative Z-ordered to another surface', 'warn');
+
+export const RELATIVE_Z_PARENT_CHIP = new Chip(
+ 'RelZParent',
+ 'Something is relative Z-ordered to this surface',
+ 'warn'
+);
+
+export const MISSING_LAYER = new Chip(
+ 'MissingLayer',
+ 'This layer was referenced from the parent, but not present in the trace',
+ 'error'
+);
+
+export const GPU_CHIP = new Chip('GPU', 'This layer was composed on the GPU', 'gpu');
+
+export const HWC_CHIP = new Chip('HWC', 'This layer was composed by Hardware Composer', 'hwc');
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/viewers/common/ime_additional_properties.ts
similarity index 66%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/viewers/common/ime_additional_properties.ts
index bfe19ce..8959d88 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/viewers/common/ime_additional_properties.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
+import {ImeLayers, ProcessedWindowManagerState} from './ime_utils';
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export class ImeAdditionalProperties {
+ constructor(
+ public wm: ProcessedWindowManagerState | undefined,
+ public sf: ImeLayers | undefined
+ ) {}
+}
diff --git a/tools/winscope/src/viewers/common/ime_ui_data.ts b/tools/winscope/src/viewers/common/ime_ui_data.ts
new file mode 100644
index 0000000..4b8d918
--- /dev/null
+++ b/tools/winscope/src/viewers/common/ime_ui_data.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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 {TraceType} from 'trace/trace_type';
+import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties';
+import {TableProperties} from 'viewers/common/table_properties';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+
+export class ImeUiData {
+ dependencies: TraceType[];
+ highlightedItems: string[] = [];
+ pinnedItems: HierarchyTreeNode[] = [];
+ hierarchyUserOptions: UserOptions = {};
+ propertiesUserOptions: UserOptions = {};
+ tree: HierarchyTreeNode | null = null;
+ propertiesTree: PropertiesTreeNode | null = null;
+ hierarchyTableProperties: TableProperties | null = null;
+ additionalProperties: ImeAdditionalProperties | null = null;
+
+ constructor(dependencies?: TraceType[]) {
+ this.dependencies = dependencies ?? [];
+ }
+}
diff --git a/tools/winscope/src/viewers/common/ime_utils.ts b/tools/winscope/src/viewers/common/ime_utils.ts
new file mode 100644
index 0000000..027da4d
--- /dev/null
+++ b/tools/winscope/src/viewers/common/ime_utils.ts
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 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 {FilterType, TreeUtils} from 'common/tree_utils';
+import {WindowContainer} from 'trace/flickerlib/common';
+import {Layer} from 'trace/flickerlib/layers/Layer';
+import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {Activity} from 'trace/flickerlib/windows/Activity';
+import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {WindowState} from 'trace/flickerlib/windows/WindowState';
+
+class ProcessedWindowManagerState {
+ constructor(
+ public name: string,
+ public stableId: string,
+ public focusedApp: string,
+ public focusedWindow: WindowState,
+ public focusedActivity: Activity,
+ public isInputMethodWindowVisible: boolean,
+ public protoImeControlTarget: any,
+ public protoImeInputTarget: any,
+ public protoImeLayeringTarget: any,
+ public protoImeInsetsSourceProvider: any,
+ public proto: any
+ ) {}
+}
+
+class ImeLayers {
+ constructor(
+ public name: string,
+ public imeContainer: Layer,
+ public inputMethodSurface: Layer,
+ public focusedWindow: Layer | undefined,
+ public taskOfImeContainer: Layer | undefined,
+ public taskOfImeSnapshot: Layer | undefined
+ ) {}
+}
+
+class ImeUtils {
+ static processWindowManagerTraceEntry(entry: WindowManagerState): ProcessedWindowManagerState {
+ const displayContent = entry.root.children[0];
+
+ return new ProcessedWindowManagerState(
+ entry.name,
+ entry.stableId,
+ entry.focusedApp,
+ entry.focusedWindow,
+ entry.focusedActivity,
+ ImeUtils.isInputMethodVisible(displayContent),
+ ImeUtils.getImeControlTargetProperty(displayContent.proto),
+ ImeUtils.getImeInputTargetProperty(displayContent.proto),
+ ImeUtils.getImeLayeringTargetProperty(displayContent.proto),
+ displayContent.proto.imeInsetsSourceProvider,
+ entry.proto
+ );
+ }
+
+ static getImeLayers(
+ entry: LayerTraceEntry,
+ processedWindowManagerState: ProcessedWindowManagerState
+ ): ImeLayers | undefined {
+ const isImeContainer = TreeUtils.makeNodeFilter('ImeContainer');
+ const imeContainer = TreeUtils.findDescendantNode(entry, isImeContainer);
+ if (!imeContainer) {
+ return undefined;
+ }
+
+ const isInputMethodSurface = TreeUtils.makeNodeFilter('InputMethod');
+ const inputMethodSurface = TreeUtils.findDescendantNode(imeContainer, isInputMethodSurface);
+
+ let focusedWindowLayer: Layer = undefined;
+ const focusedWindowToken = processedWindowManagerState.focusedWindow?.token;
+ if (focusedWindowToken) {
+ const isFocusedWindow = TreeUtils.makeNodeFilter(focusedWindowToken);
+ focusedWindowLayer = TreeUtils.findDescendantNode(entry, isFocusedWindow);
+ }
+
+ // we want to see both ImeContainer and IME-snapshot if there are
+ // cases where both exist
+ const taskLayerOfImeContainer = ImeUtils.findAncestorTaskLayerOfImeLayer(
+ entry,
+ TreeUtils.makeNodeFilter('ImeContainer')
+ );
+
+ const taskLayerOfImeSnapshot = ImeUtils.findAncestorTaskLayerOfImeLayer(
+ entry,
+ TreeUtils.makeNodeFilter('IME-snapshot')
+ );
+
+ return new ImeLayers(
+ entry.name,
+ imeContainer,
+ inputMethodSurface,
+ focusedWindowLayer,
+ taskLayerOfImeContainer,
+ taskLayerOfImeSnapshot
+ );
+ }
+
+ static transformInputConnectionCall(entry: any) {
+ 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;
+ }
+
+ private static findAncestorTaskLayerOfImeLayer(
+ entry: LayerTraceEntry,
+ isTargetImeLayer: FilterType
+ ): Layer {
+ const imeLayer = TreeUtils.findDescendantNode(entry, isTargetImeLayer);
+ if (!imeLayer) {
+ return undefined;
+ }
+
+ const isTaskLayer = TreeUtils.makeNodeFilter('Task|ImePlaceholder');
+ const taskLayer = TreeUtils.findAncestorNode(imeLayer, isTaskLayer) as Layer;
+ if (!taskLayer) {
+ return undefined;
+ }
+
+ taskLayer.kind = 'SF subtree - ' + taskLayer.id;
+ return taskLayer;
+ }
+
+ private static getImeControlTargetProperty(displayContentProto: any): any {
+ const POSSIBLE_NAMES = ['inputMethodControlTarget', 'imeControlTarget'];
+ return ImeUtils.findAnyPropertyWithMatchingName(displayContentProto, POSSIBLE_NAMES);
+ }
+
+ private static getImeInputTargetProperty(displayContentProto: any): any {
+ const POSSIBLE_NAMES = ['inputMethodInputTarget', 'imeInputTarget'];
+ return ImeUtils.findAnyPropertyWithMatchingName(displayContentProto, POSSIBLE_NAMES);
+ }
+
+ private static getImeLayeringTargetProperty(displayContentProto: any): any {
+ const POSSIBLE_NAMES = ['inputMethodTarget', 'imeLayeringTarget'];
+ return ImeUtils.findAnyPropertyWithMatchingName(displayContentProto, POSSIBLE_NAMES);
+ }
+
+ private static findAnyPropertyWithMatchingName(object: any, possible_names: string[]): any {
+ const key = Object.keys(object).find((key) => possible_names.includes(key));
+ return key ? object[key] : undefined;
+ }
+
+ private static isInputMethodVisible(displayContent: WindowContainer): boolean {
+ const isInputMethod = TreeUtils.makeNodeFilter('InputMethod');
+ const inputMethodWindowOrLayer = TreeUtils.findDescendantNode(
+ displayContent,
+ isInputMethod
+ ) as WindowContainer;
+ return inputMethodWindowOrLayer?.isVisible === true;
+ }
+}
+
+export {ImeUtils, ProcessedWindowManagerState, ImeLayers};
diff --git a/tools/winscope/src/viewers/common/ime_utils_test.ts b/tools/winscope/src/viewers/common/ime_utils_test.ts
new file mode 100644
index 0000000..ef6de14
--- /dev/null
+++ b/tools/winscope/src/viewers/common/ime_utils_test.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 {UnitTestUtils} from 'test/unit/utils';
+import {TraceType} from 'trace/trace_type';
+import {ImeUtils} from './ime_utils';
+
+describe('ImeUtils', () => {
+ it('processes WindowManager trace entry', async () => {
+ const entries = await UnitTestUtils.getImeTraceEntries();
+ const processed = ImeUtils.processWindowManagerTraceEntry(
+ entries.get(TraceType.WINDOW_MANAGER)
+ );
+
+ expect(processed.focusedApp).toEqual(
+ 'com.google.android.apps.messaging/.ui.search.ZeroStateSearchActivity'
+ );
+
+ expect(processed.focusedActivity.token).toEqual('9d8c2ef');
+ expect(processed.focusedActivity.layerId).toEqual(260);
+
+ expect(processed.focusedWindow.token).toEqual('928b3d');
+ expect(processed.focusedWindow.title).toEqual(
+ 'com.google.android.apps.messaging/com.google.android.apps.messaging.ui.search.ZeroStateSearchActivity'
+ );
+
+ expect(processed.protoImeControlTarget.windowContainer.identifier.title).toEqual(
+ 'com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity'
+ );
+ expect(processed.protoImeControlTarget.windowContainer.identifier.hashCode).toEqual(247026562);
+
+ expect(processed.protoImeInputTarget.windowContainer.identifier.title).toEqual(
+ 'com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity'
+ );
+ expect(processed.protoImeInputTarget.windowContainer.identifier.hashCode).toEqual(247026562);
+
+ expect(processed.protoImeInsetsSourceProvider.insetsSourceProvider).toBeDefined();
+
+ expect(processed.protoImeLayeringTarget.windowContainer.identifier.title).toEqual(
+ 'SnapshotStartingWindow for taskId=1393'
+ );
+ expect(processed.protoImeLayeringTarget.windowContainer.identifier.hashCode).toEqual(222907471);
+
+ expect(processed.isInputMethodWindowVisible).toBeFalse();
+ });
+
+ it('processes SurfaceFlinger trace entry', async () => {
+ const entries = await UnitTestUtils.getImeTraceEntries();
+ const processedWindowManagerState = ImeUtils.processWindowManagerTraceEntry(
+ entries.get(TraceType.WINDOW_MANAGER)
+ );
+ const layers = ImeUtils.getImeLayers(
+ entries.get(TraceType.SURFACE_FLINGER),
+ processedWindowManagerState
+ )!;
+
+ expect(layers.inputMethodSurface.id).toEqual(280);
+ expect(layers.inputMethodSurface.isVisible).toEqual(false);
+ expect(layers.inputMethodSurface.rect.label).toEqual(
+ 'Surface(name=77f1069 InputMethod)/@0xb4afb8f - animation-leash of insets_animation#280'
+ );
+ expect(layers.inputMethodSurface.screenBounds).toBeDefined();
+
+ expect(layers.imeContainer.id).toEqual(12);
+ expect(layers.imeContainer.z).toEqual(1);
+ expect(layers.imeContainer.zOrderRelativeOfId).toEqual(115);
+
+ expect(String(layers.focusedWindow.color)).toEqual('r:0 g:0 b:0 a:1');
+
+ expect(layers.taskOfImeContainer.kind).toEqual('SF subtree - 114');
+ expect(layers.taskOfImeContainer.name).toEqual('Task=1391#114');
+
+ expect(layers.taskOfImeSnapshot).toBeUndefined();
+ });
+});
diff --git a/tools/winscope/src/viewers/common/presenter_input_method.ts b/tools/winscope/src/viewers/common/presenter_input_method.ts
new file mode 100644
index 0000000..6013376
--- /dev/null
+++ b/tools/winscope/src/viewers/common/presenter_input_method.ts
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2022 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 {PersistentStoreProxy} from 'common/persistent_store_proxy';
+import {FilterType, TreeUtils} from 'common/tree_utils';
+import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Trace, TraceEntry} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TraceType} from 'trace/trace_type';
+import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties';
+import {ImeUiData} from 'viewers/common/ime_ui_data';
+import {ImeLayers, ImeUtils, ProcessedWindowManagerState} from 'viewers/common/ime_utils';
+import {TableProperties} from 'viewers/common/table_properties';
+import {TreeGenerator} from 'viewers/common/tree_generator';
+import {TreeTransformer} from 'viewers/common/tree_transformer';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+
+type NotifyImeViewCallbackType = (uiData: ImeUiData) => void;
+
+export abstract class PresenterInputMethod {
+ private readonly imeTrace: Trace<object>;
+ private readonly wmTrace?: Trace<WindowManagerState>;
+ private readonly sfTrace?: Trace<LayerTraceEntry>;
+ private hierarchyFilter: FilterType = TreeUtils.makeNodeFilter('');
+ private propertiesFilter: FilterType = TreeUtils.makeNodeFilter('');
+ private pinnedItems: HierarchyTreeNode[] = [];
+ private pinnedIds: string[] = [];
+ private selectedHierarchyTree: HierarchyTreeNode | null = null;
+
+ readonly notifyViewCallback: NotifyImeViewCallbackType;
+ protected readonly dependencies: TraceType[];
+ protected uiData: ImeUiData;
+ protected highlightedItems: string[] = [];
+ protected entry: TraceTreeNode | null = null;
+ protected additionalPropertyEntry: TraceTreeNode | null = null;
+ protected hierarchyUserOptions: UserOptions = PersistentStoreProxy.new<UserOptions>(
+ 'ImeHierarchyOptions',
+ {
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: false,
+ },
+ },
+ this.storage
+ );
+ protected propertiesUserOptions: UserOptions = PersistentStoreProxy.new<UserOptions>(
+ 'ImePropertiesOptions',
+ {
+ showDefaults: {
+ name: 'Show defaults',
+ enabled: false,
+ tooltip: `
+ If checked, shows the value of all properties.
+ Otherwise, hides all properties whose value is
+ the default for its data type.
+ `,
+ },
+ },
+ this.storage
+ );
+
+ constructor(
+ traces: Traces,
+ private storage: Storage,
+ dependencies: TraceType[],
+ notifyViewCallback: NotifyImeViewCallbackType
+ ) {
+ this.imeTrace = traces.getTrace(dependencies[0]) as Trace<TraceTreeNode>;
+ this.sfTrace = traces.getTrace(TraceType.SURFACE_FLINGER);
+ this.wmTrace = traces.getTrace(TraceType.WINDOW_MANAGER);
+
+ this.dependencies = dependencies;
+ this.notifyViewCallback = notifyViewCallback;
+ this.uiData = new ImeUiData(dependencies);
+ this.notifyViewCallback(this.uiData);
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.uiData = new ImeUiData(this.dependencies);
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+
+ const [imeEntry, sfEntry, wmEntry] = this.findTraceEntries(position);
+
+ if (imeEntry) {
+ this.entry = imeEntry.getValue() as TraceTreeNode;
+ this.uiData.highlightedItems = this.highlightedItems;
+ this.uiData.additionalProperties = this.getAdditionalProperties(
+ wmEntry?.getValue(),
+ sfEntry?.getValue()
+ );
+ this.uiData.tree = this.generateTree();
+ this.uiData.hierarchyTableProperties = this.updateHierarchyTableProperties();
+ }
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updatePinnedItems(pinnedItem: HierarchyTreeNode) {
+ const pinnedId = `${pinnedItem.id}`;
+ if (this.pinnedItems.map((item) => `${item.id}`).includes(pinnedId)) {
+ this.pinnedItems = this.pinnedItems.filter((pinned) => `${pinned.id}` !== pinnedId);
+ } else {
+ this.pinnedItems.push(pinnedItem);
+ }
+ this.updatePinnedIds(pinnedId);
+ this.uiData.pinnedItems = this.pinnedItems;
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updateHighlightedItems(id: string) {
+ if (this.highlightedItems.includes(id)) {
+ this.highlightedItems = this.highlightedItems.filter((hl) => hl !== id);
+ } else {
+ this.highlightedItems = []; //if multi-select surfaces implemented, remove this line
+ this.highlightedItems.push(id);
+ }
+ this.uiData.highlightedItems = this.highlightedItems;
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updateHierarchyTree(userOptions: UserOptions) {
+ this.hierarchyUserOptions = userOptions;
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.tree = this.generateTree();
+ this.notifyViewCallback(this.uiData);
+ }
+
+ filterHierarchyTree(filterString: string) {
+ this.hierarchyFilter = TreeUtils.makeNodeFilter(filterString);
+ this.uiData.tree = this.generateTree();
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updatePropertiesTree(userOptions: UserOptions) {
+ this.propertiesUserOptions = userOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+ this.updateSelectedTreeUiData();
+ }
+
+ filterPropertiesTree(filterString: string) {
+ this.propertiesFilter = TreeUtils.makeNodeFilter(filterString);
+ this.updateSelectedTreeUiData();
+ }
+
+ newPropertiesTree(selectedItem: HierarchyTreeNode) {
+ this.additionalPropertyEntry = null;
+ this.selectedHierarchyTree = selectedItem;
+ this.updateSelectedTreeUiData();
+ }
+
+ newAdditionalPropertiesTree(selectedItem: any) {
+ this.selectedHierarchyTree = new HierarchyTreeNode(
+ selectedItem.name,
+ 'AdditionalProperty',
+ 'AdditionalProperty'
+ );
+ this.additionalPropertyEntry = {
+ name: selectedItem.name,
+ kind: 'AdditionalProperty',
+ children: [],
+ stableId: 'AdditionalProperty',
+ proto: selectedItem.proto,
+ };
+ this.updateSelectedTreeUiData();
+ }
+
+ protected getAdditionalProperties(
+ wmEntry: TraceTreeNode | undefined,
+ sfEntry: TraceTreeNode | undefined
+ ) {
+ let wmProperties: ProcessedWindowManagerState | undefined;
+ let sfProperties: ImeLayers | undefined;
+ let sfSubtrees: any[];
+
+ if (wmEntry) {
+ wmProperties = ImeUtils.processWindowManagerTraceEntry(wmEntry);
+
+ if (sfEntry) {
+ sfProperties = ImeUtils.getImeLayers(sfEntry, wmProperties);
+ sfSubtrees = [sfProperties?.taskOfImeContainer, sfProperties?.taskOfImeSnapshot]
+ .filter((node) => node) // filter away null values
+ .map((node) => {
+ node.kind = 'SF subtree - ' + node.id;
+ return node;
+ });
+ this.entry?.children.push(...sfSubtrees);
+ }
+ }
+
+ return new ImeAdditionalProperties(wmProperties, sfProperties);
+ }
+
+ protected generateTree() {
+ if (!this.entry) {
+ return null;
+ }
+
+ const generator = new TreeGenerator(this.entry, this.hierarchyFilter, this.pinnedIds)
+ .setIsOnlyVisibleView(this.hierarchyUserOptions['onlyVisible']?.enabled)
+ .setIsSimplifyNames(this.hierarchyUserOptions['simplifyNames']?.enabled)
+ .setIsFlatView(this.hierarchyUserOptions['flat']?.enabled)
+ .withUniqueNodeId();
+ const tree: HierarchyTreeNode | null = generator.generateTree();
+ this.pinnedItems = generator.getPinnedItems();
+ this.uiData.pinnedItems = this.pinnedItems;
+ return tree;
+ }
+
+ private updateSelectedTreeUiData() {
+ if (this.selectedHierarchyTree) {
+ this.uiData.propertiesTree = this.getTreeWithTransformedProperties(
+ this.selectedHierarchyTree
+ );
+ }
+ this.notifyViewCallback(this.uiData);
+ }
+ private updatePinnedIds(newId: string) {
+ if (this.pinnedIds.includes(newId)) {
+ this.pinnedIds = this.pinnedIds.filter((pinned) => pinned !== newId);
+ } else {
+ this.pinnedIds.push(newId);
+ }
+ }
+
+ private getTreeWithTransformedProperties(selectedTree: HierarchyTreeNode): PropertiesTreeNode {
+ const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
+ .setOnlyProtoDump(this.additionalPropertyEntry != null)
+ .setIsShowDefaults(this.propertiesUserOptions['showDefaults']?.enabled)
+ .setTransformerOptions({skip: selectedTree.skip})
+ .setProperties(this.additionalPropertyEntry ?? this.entry);
+ const transformedTree = transformer.transform();
+ return transformedTree;
+ }
+
+ private findTraceEntries(
+ position: TracePosition
+ ): [
+ TraceEntry<object> | undefined,
+ TraceEntry<LayerTraceEntry> | undefined,
+ TraceEntry<WindowManagerState> | undefined
+ ] {
+ const imeEntry = TraceEntryFinder.findCorrespondingEntry(this.imeTrace, position);
+ if (!imeEntry) {
+ return [undefined, undefined, undefined];
+ }
+
+ if (!this.imeTrace.hasFrameInfo()) {
+ return [imeEntry, undefined, undefined];
+ }
+
+ const frames = imeEntry.getFramesRange();
+ if (!frames || frames.start === frames.end) {
+ return [imeEntry, undefined, undefined];
+ }
+
+ const frame = frames.start;
+ const sfEntry = this.sfTrace?.getFrame(frame)?.findClosestEntry(imeEntry.getTimestamp());
+ const wmEntry = this.wmTrace?.getFrame(frame)?.findClosestEntry(imeEntry.getTimestamp());
+
+ return [imeEntry, sfEntry, wmEntry];
+ }
+
+ protected abstract updateHierarchyTableProperties(): TableProperties;
+}
diff --git a/tools/winscope/src/viewers/common/presenter_input_method_test_utils.ts b/tools/winscope/src/viewers/common/presenter_input_method_test_utils.ts
new file mode 100644
index 0000000..9902b6c
--- /dev/null
+++ b/tools/winscope/src/viewers/common/presenter_input_method_test_utils.ts
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2022 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.d
+ */
+
+import {assertDefined} from 'common/assert_utils';
+import {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {MockStorage} from 'test/unit/mock_storage';
+import {TracesBuilder} from 'test/unit/traces_builder';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {UnitTestUtils} from 'test/unit/utils';
+import {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {ImeUiData} from 'viewers/common/ime_ui_data';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {PresenterInputMethodClients} from 'viewers/viewer_input_method_clients/presenter_input_method_clients';
+import {PresenterInputMethodManagerService} from 'viewers/viewer_input_method_manager_service/presenter_input_method_manager_service';
+import {PresenterInputMethodService} from 'viewers/viewer_input_method_service/presenter_input_method_service';
+import {PresenterInputMethod} from './presenter_input_method';
+
+export function executePresenterInputMethodTests(
+ selected: HierarchyTreeNode,
+ propertiesTreeFilterString: string,
+ expectedChildren: [number, number],
+ expectHierarchyTreeWithSfSubtree: boolean,
+ PresenterInputMethod:
+ | typeof PresenterInputMethodClients
+ | typeof PresenterInputMethodService
+ | typeof PresenterInputMethodManagerService,
+ imeTraceType: TraceType
+) {
+ describe('PresenterInputMethod', () => {
+ let presenter: PresenterInputMethod;
+ let uiData: ImeUiData;
+ let position: TracePosition;
+ let selectedTree: HierarchyTreeNode;
+
+ beforeEach(async () => {
+ selectedTree = selected;
+ await setUpTestEnvironment([
+ imeTraceType,
+ TraceType.SURFACE_FLINGER,
+ TraceType.WINDOW_MANAGER,
+ ]);
+ });
+
+ it('is robust to empty trace', () => {
+ const traces = new TracesBuilder().setEntries(imeTraceType, []).build();
+ presenter = createPresenter(traces);
+
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.propertiesUserOptions).toBeTruthy();
+ expect(uiData.tree).toBeFalsy();
+
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.propertiesUserOptions).toBeTruthy();
+ expect(uiData.tree).toBeFalsy();
+ });
+
+ it('is robust to traces without SF', async () => {
+ await setUpTestEnvironment([imeTraceType, TraceType.WINDOW_MANAGER]);
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.propertiesUserOptions).toBeTruthy();
+ expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
+ });
+
+ it('is robust to traces without WM', async () => {
+ await setUpTestEnvironment([imeTraceType, TraceType.SURFACE_FLINGER]);
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.propertiesUserOptions).toBeTruthy();
+ expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
+ });
+
+ it('is robust to traces without WM and SF', async () => {
+ await setUpTestEnvironment([imeTraceType]);
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.propertiesUserOptions).toBeTruthy();
+ expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
+ });
+
+ it('processes trace position updates', () => {
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.propertiesUserOptions).toBeTruthy();
+ expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
+ });
+
+ it('can update pinned items', () => {
+ expect(uiData.pinnedItems).toEqual([]);
+ const pinnedItem = new HierarchyTreeBuilder()
+ .setName('FirstPinnedItem')
+ .setStableId('TestItem 4')
+ .setLayerId(4)
+ .build();
+ presenter.updatePinnedItems(pinnedItem);
+ expect(uiData.pinnedItems).toContain(pinnedItem);
+ });
+
+ it('can update highlighted items', () => {
+ expect(uiData.highlightedItems).toEqual([]);
+ const id = 'entry';
+ presenter.updateHighlightedItems(id);
+ expect(uiData.highlightedItems).toContain(id);
+ });
+
+ it('can update hierarchy tree', () => {
+ //change flat view to true
+ const userOptions: UserOptions = {
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: true,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: false,
+ },
+ };
+
+ let expectedChildren = expectHierarchyTreeWithSfSubtree ? 2 : 1;
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.tree?.children.length).toEqual(expectedChildren);
+
+ // Filter out non-visible child
+ expectedChildren = expectHierarchyTreeWithSfSubtree ? 1 : 0;
+ presenter.updateHierarchyTree(userOptions);
+ expect(uiData.hierarchyUserOptions).toEqual(userOptions);
+ expect(uiData.tree?.children.length).toEqual(expectedChildren);
+ });
+
+ it('can filter hierarchy tree', () => {
+ const userOptions: UserOptions = {
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: true,
+ },
+ };
+
+ const expectedChildren = expectHierarchyTreeWithSfSubtree ? 12 : 1;
+ presenter.onTracePositionUpdate(position);
+ presenter.updateHierarchyTree(userOptions);
+ expect(uiData.tree?.children.length).toEqual(expectedChildren);
+
+ // Filter out all children
+ presenter.filterHierarchyTree('Reject all');
+ expect(uiData.tree?.children.length).toEqual(0);
+ });
+
+ it('can set new properties tree and associated ui data', () => {
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ // does not check specific tree values as tree transformation method may change
+ expect(uiData.propertiesTree).toBeTruthy();
+ });
+
+ it('can filter properties tree', () => {
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ let nonTerminalChildren =
+ uiData.propertiesTree?.children?.filter(
+ (child: PropertiesTreeNode) => typeof child.propertyKey === 'string'
+ ) ?? [];
+
+ expect(nonTerminalChildren.length).toEqual(expectedChildren[0]);
+ presenter.filterPropertiesTree(propertiesTreeFilterString);
+
+ nonTerminalChildren =
+ uiData.propertiesTree?.children?.filter(
+ (child: PropertiesTreeNode) => typeof child.propertyKey === 'string'
+ ) ?? [];
+ expect(nonTerminalChildren.length).toEqual(expectedChildren[1]);
+ });
+
+ const setUpTestEnvironment = async (traceTypes: TraceType[]) => {
+ const traces = new Traces();
+ const entries = await UnitTestUtils.getImeTraceEntries();
+
+ traceTypes.forEach((traceType) => {
+ const trace = new TraceBuilder<object>()
+ .setEntries([entries.get(traceType)])
+ .setFrame(0, 0)
+ .build();
+ traces.setTrace(traceType, trace);
+ });
+
+ presenter = createPresenter(traces);
+
+ position = TracePosition.fromTraceEntry(
+ assertDefined(traces.getTrace(imeTraceType)).getEntry(0)
+ );
+ };
+
+ const createPresenter = (traces: Traces): PresenterInputMethod => {
+ return new PresenterInputMethod(
+ traces,
+ new MockStorage(),
+ [imeTraceType],
+ (newData: ImeUiData) => {
+ uiData = newData;
+ }
+ );
+ };
+ });
+}
diff --git a/tools/winscope/src/viewers/common/properties_tree_generator.ts b/tools/winscope/src/viewers/common/properties_tree_generator.ts
new file mode 100644
index 0000000..32c6740
--- /dev/null
+++ b/tools/winscope/src/viewers/common/properties_tree_generator.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 {PropertiesTreeNode} from './ui_tree_utils';
+
+class PropertiesTreeGenerator {
+ generate(key: string, value: any): PropertiesTreeNode {
+ if (this.isLeaf(value)) {
+ return {
+ propertyKey: key,
+ propertyValue: this.leafToString(value)!,
+ };
+ }
+
+ let children: PropertiesTreeNode[];
+
+ if (Array.isArray(value)) {
+ children = value.map((element, index) => this.generate('' + index, element));
+ } else {
+ children = Object.keys(value).map((childName) => this.generate(childName, value[childName]));
+ }
+
+ return {
+ propertyKey: key,
+ children,
+ };
+ }
+
+ private isLeaf(value: any): boolean {
+ return this.leafToString(value) !== undefined;
+ }
+
+ private leafToString(value: any): undefined | string {
+ if (value == null) {
+ return '';
+ }
+ if (typeof value === 'boolean') {
+ return '' + value;
+ }
+ if (typeof value === 'number') {
+ return '' + value;
+ }
+ if (typeof value === 'string') {
+ return value;
+ }
+ if (this.isLong(value)) {
+ return value.toString();
+ }
+ if (Array.isArray(value) && value.length === 0) {
+ return '[]';
+ }
+ if (typeof value === 'object' && Object.keys(value).length === 0) {
+ return '{}';
+ }
+ return undefined;
+ }
+
+ private isLong(value: any) {
+ return (
+ Object.prototype.hasOwnProperty.call(value, 'high') &&
+ Object.prototype.hasOwnProperty.call(value, 'low') &&
+ Object.prototype.hasOwnProperty.call(value, 'unsigned')
+ );
+ }
+}
+
+export {PropertiesTreeGenerator};
diff --git a/tools/winscope/src/viewers/common/properties_tree_generator_test.ts b/tools/winscope/src/viewers/common/properties_tree_generator_test.ts
new file mode 100644
index 0000000..389aaa9
--- /dev/null
+++ b/tools/winscope/src/viewers/common/properties_tree_generator_test.ts
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 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 Long from 'long';
+import {PropertiesTreeGenerator} from 'viewers/common/properties_tree_generator';
+import {PropertiesTreeNode} from './ui_tree_utils';
+
+describe('PropertiesTreeGenerator', () => {
+ it('handles boolean', () => {
+ const input = true;
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ propertyValue: 'true',
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles number', () => {
+ const input = 10;
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ propertyValue: '10',
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles longs', () => {
+ const input = new Long(10, 100, false);
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ propertyValue: '429496729610',
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles string', () => {
+ const input = 'value';
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ propertyValue: 'value',
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles empty array', () => {
+ const input: any[] = [];
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ propertyValue: '[]',
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles array', () => {
+ const input = ['value0', 'value1'];
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ children: [
+ {
+ propertyKey: '0',
+ propertyValue: 'value0',
+ },
+ {
+ propertyKey: '1',
+ propertyValue: 'value1',
+ },
+ ],
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles empty object', () => {
+ const input = {};
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ propertyValue: '{}',
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles object', () => {
+ const input = {
+ key0: 'value0',
+ key1: 'value1',
+ };
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ children: [
+ {
+ propertyKey: 'key0',
+ propertyValue: 'value0',
+ },
+ {
+ propertyKey: 'key1',
+ propertyValue: 'value1',
+ },
+ ],
+ };
+
+ expect(actual).toEqual(expected);
+ });
+
+ it('handles nested objects', () => {
+ const input = {
+ object: {
+ key: 'object_value',
+ },
+ array: ['array_value'],
+ };
+ const actual = new PropertiesTreeGenerator().generate('root', input);
+
+ const expected: PropertiesTreeNode = {
+ propertyKey: 'root',
+ children: [
+ {
+ propertyKey: 'object',
+ children: [
+ {
+ propertyKey: 'key',
+ propertyValue: 'object_value',
+ },
+ ],
+ },
+ {
+ propertyKey: 'array',
+ children: [
+ {
+ propertyKey: '0',
+ propertyValue: 'array_value',
+ },
+ ],
+ },
+ ],
+ };
+
+ expect(actual).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/viewers/common/rectangle.ts b/tools/winscope/src/viewers/common/rectangle.ts
new file mode 100644
index 0000000..5a8817a
--- /dev/null
+++ b/tools/winscope/src/viewers/common/rectangle.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 interface Rectangle {
+ topLeft: Point;
+ bottomRight: Point;
+ label: string;
+ transform: RectTransform | null;
+ isVisible: boolean;
+ isDisplay: boolean;
+ ref: any;
+ id: string;
+ displayId: number;
+ isVirtual: boolean;
+ isClickable: boolean;
+ cornerRadius: number;
+}
+
+export interface Point {
+ x: number;
+ y: number;
+}
+
+export interface Size {
+ width: number;
+ height: number;
+}
+
+export interface RectTransform {
+ matrix?: RectMatrix;
+ dsdx?: number;
+ dsdy?: number;
+ dtdx?: number;
+ dtdy?: number;
+ tx?: number;
+ ty?: number;
+}
+
+export interface RectMatrix {
+ dsdx: number;
+ dsdy: number;
+ dtdx: number;
+ dtdy: number;
+ tx: number;
+ ty: number;
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/viewers/common/table_properties.ts
similarity index 76%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/viewers/common/table_properties.ts
index bfe19ce..119b6e3 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/viewers/common/table_properties.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface TableProperties {
+ [key: string]: string | boolean | undefined;
+}
diff --git a/tools/winscope/src/viewers/common/tree_generator.ts b/tools/winscope/src/viewers/common/tree_generator.ts
new file mode 100644
index 0000000..51f9207
--- /dev/null
+++ b/tools/winscope/src/viewers/common/tree_generator.ts
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2022 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 {FilterType, TreeNode} from 'common/tree_utils';
+import {ObjectFormatter} from 'trace/flickerlib/ObjectFormatter';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {
+ GPU_CHIP,
+ HWC_CHIP,
+ MISSING_LAYER,
+ RELATIVE_Z_CHIP,
+ RELATIVE_Z_PARENT_CHIP,
+ VISIBLE_CHIP,
+} from 'viewers/common/chip';
+import {DiffType, HierarchyTreeNode, UiTreeUtils} from './ui_tree_utils';
+
+type GetNodeIdCallbackType = (node: TraceTreeNode | null) => string | null;
+type IsModifiedCallbackType = (
+ newTree: TraceTreeNode | null,
+ oldTree: TraceTreeNode | null
+) => boolean;
+
+const HwcCompositionType = {
+ CLIENT: 1,
+ DEVICE: 2,
+ SOLID_COLOR: 3,
+};
+
+export class TreeGenerator {
+ private isOnlyVisibleView = false;
+ private isSimplifyNames = false;
+ private isFlatView = false;
+ private filter: FilterType;
+ private inputEntry: TraceTreeNode;
+ private previousEntry: TraceTreeNode | null = null;
+ private getNodeId?: GetNodeIdCallbackType;
+ private isModified?: IsModifiedCallbackType;
+ private newMapping: Map<string, TraceTreeNode> | null = null;
+ private oldMapping: Map<string, TraceTreeNode> | null = null;
+ private readonly pinnedIds: string[];
+ private pinnedItems: HierarchyTreeNode[] = [];
+ private relZParentIds: string[] = [];
+ private flattenedChildren: HierarchyTreeNode[] = [];
+
+ constructor(inputEntry: TraceTreeNode, filter: FilterType, pinnedIds?: string[]) {
+ this.inputEntry = inputEntry;
+ this.filter = filter;
+ this.pinnedIds = pinnedIds ?? [];
+ }
+
+ setIsOnlyVisibleView(enabled: boolean): TreeGenerator {
+ this.isOnlyVisibleView = enabled;
+ return this;
+ }
+
+ setIsSimplifyNames(enabled: boolean): TreeGenerator {
+ this.isSimplifyNames = enabled;
+ return this;
+ }
+
+ setIsFlatView(enabled: boolean): TreeGenerator {
+ this.isFlatView = enabled;
+ return this;
+ }
+
+ generateTree(): HierarchyTreeNode | null {
+ return this.getCustomizedTree(this.inputEntry);
+ }
+
+ compareWith(previousEntry: TraceTreeNode | null): TreeGenerator {
+ this.previousEntry = previousEntry;
+ return this;
+ }
+
+ withUniqueNodeId(getNodeId?: GetNodeIdCallbackType): TreeGenerator {
+ this.getNodeId = (node: TraceTreeNode | null) => {
+ const id = getNodeId ? getNodeId(node) : this.defaultNodeIdCallback(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?: IsModifiedCallbackType): TreeGenerator {
+ this.isModified = isModified ?? this.defaultModifiedCheck;
+ return this;
+ }
+
+ generateFinalTreeWithDiff(): HierarchyTreeNode | null {
+ this.newMapping = this.generateIdToNodeMapping(this.inputEntry);
+ this.oldMapping = this.previousEntry ? this.generateIdToNodeMapping(this.previousEntry) : null;
+
+ const diffTrees = this.generateDiffTree(this.inputEntry, this.previousEntry, [], []);
+
+ let diffTree: TraceTreeNode;
+ if (diffTrees.length > 1) {
+ diffTree = {
+ kind: '',
+ name: 'DiffTree',
+ parent: undefined,
+ children: diffTrees,
+ stableId: 'DiffTree',
+ };
+ } else {
+ diffTree = diffTrees[0];
+ }
+ return this.getCustomizedTree(diffTree);
+ }
+
+ private getCustomizedTree(tree: TraceTreeNode | null): HierarchyTreeNode | null {
+ if (!tree) return null;
+ let newTree = this.generateTreeWithUserOptions(tree, false);
+ if (!newTree) return null;
+ newTree = this.updateTreeWithRelZParentChips(newTree);
+
+ if (this.isFlatView && newTree.children) {
+ this.flattenChildren(newTree.children);
+ newTree.children = this.flattenedChildren;
+ }
+ return Object.freeze(newTree);
+ }
+
+ getPinnedItems(): HierarchyTreeNode[] {
+ return this.pinnedItems;
+ }
+
+ private flattenChildren(children: HierarchyTreeNode[]) {
+ for (let i = 0; i < children.length; i++) {
+ const child = children[i];
+ const childIsVisibleNode =
+ child.isVisible && UiTreeUtils.isVisibleNode(child.kind, child.type);
+ const showInOnlyVisibleView = this.isOnlyVisibleView && childIsVisibleNode;
+ const passVisibleCheck = !this.isOnlyVisibleView || showInOnlyVisibleView;
+ if (this.filterMatches(child) && passVisibleCheck) {
+ this.flattenedChildren.push(child);
+ }
+ if (child.children) {
+ this.flattenChildren(child.children);
+ }
+ }
+ }
+
+ private filterMatches(item?: TreeNode | null): boolean {
+ return this.filter(item) ?? false;
+ }
+
+ private generateTreeWithUserOptions(
+ tree: TraceTreeNode,
+ parentFilterMatch: boolean
+ ): HierarchyTreeNode | null {
+ return this.applyChecks(tree, parentFilterMatch);
+ }
+
+ private updateTreeWithRelZParentChips(tree: HierarchyTreeNode): HierarchyTreeNode {
+ return this.applyRelZParentCheck(tree);
+ }
+
+ private applyRelZParentCheck(tree: HierarchyTreeNode) {
+ if (tree.id && this.relZParentIds.includes(`${tree.id}`)) {
+ tree.chips.push(RELATIVE_Z_PARENT_CHIP);
+ }
+
+ const numOfChildren = tree.children?.length ?? 0;
+ for (let i = 0; i < numOfChildren; i++) {
+ tree.children[i] = this.updateTreeWithRelZParentChips(tree.children[i]);
+ }
+
+ return tree;
+ }
+
+ private addChips(tree: HierarchyTreeNode): HierarchyTreeNode {
+ if (tree.hwcCompositionType === HwcCompositionType.CLIENT) {
+ tree.chips.push(GPU_CHIP);
+ } else if (
+ tree.hwcCompositionType === HwcCompositionType.DEVICE ||
+ tree.hwcCompositionType === HwcCompositionType.SOLID_COLOR
+ ) {
+ tree.chips.push(HWC_CHIP);
+ }
+ if (tree.isVisible && UiTreeUtils.isVisibleNode(tree.kind, tree.type)) {
+ tree.chips.push(VISIBLE_CHIP);
+ }
+ if (
+ tree.zOrderRelativeOfId !== undefined &&
+ tree.zOrderRelativeOfId !== -1 &&
+ !UiTreeUtils.isParentNode(tree.kind) &&
+ !tree.isRootLayer
+ ) {
+ tree.chips.push(RELATIVE_Z_CHIP);
+ this.relZParentIds.push(`${tree.zOrderRelativeOfId}`);
+ }
+ if (tree.isMissing) {
+ tree.chips.push(MISSING_LAYER);
+ }
+ return tree;
+ }
+
+ private applyChecks(tree: TraceTreeNode, parentFilterMatch: boolean): HierarchyTreeNode | null {
+ let newTree = this.makeTreeNode(tree);
+
+ // add id field to tree if id does not exist (e.g. for WM traces)
+ if (!newTree?.id && newTree?.layerId) {
+ newTree.id = newTree.layerId;
+ }
+
+ // simplify names check
+ newTree.simplifyNames = this.isSimplifyNames;
+
+ // check item either matches filter, or has parents/children matching filter
+ if (UiTreeUtils.isParentNode(tree.kind) || parentFilterMatch) {
+ newTree.showInFilteredView = true;
+ } else {
+ newTree.showInFilteredView = this.filterMatches(tree);
+ parentFilterMatch = newTree.showInFilteredView;
+ }
+
+ if (this.isOnlyVisibleView) {
+ newTree.showInOnlyVisibleView = UiTreeUtils.isParentNode(tree.kind)
+ ? true
+ : newTree.isVisible;
+ }
+
+ newTree.children = [];
+ const numOfChildren = tree.children?.length ?? 0;
+ for (let i = 0; i < numOfChildren; i++) {
+ const child = tree.children[i];
+ const newTreeChild = this.generateTreeWithUserOptions(child, parentFilterMatch);
+
+ if (newTreeChild) {
+ if (newTreeChild.showInFilteredView) {
+ newTree.showInFilteredView = true;
+ }
+
+ if (this.isOnlyVisibleView && newTreeChild.showInOnlyVisibleView) {
+ newTree.showInOnlyVisibleView = true;
+ }
+
+ newTree.children.push(newTreeChild);
+ }
+ }
+
+ const doNotShowInOnlyVisibleView = this.isOnlyVisibleView && !newTree.showInOnlyVisibleView;
+ if (!newTree.showInFilteredView || doNotShowInOnlyVisibleView) {
+ return null;
+ }
+
+ newTree = this.addChips(newTree);
+
+ if (this.pinnedIds.includes(`${newTree.id}`)) {
+ this.pinnedItems.push(newTree);
+ }
+
+ return newTree;
+ }
+
+ private generateIdToNodeMapping(
+ node: TraceTreeNode,
+ acc?: Map<string, TraceTreeNode>
+ ): Map<string, TraceTreeNode> {
+ acc = acc || new Map<string, TraceTreeNode>();
+
+ const nodeId: string = this.getNodeId!(node)!;
+
+ if (acc.get(nodeId)) {
+ throw new Error(`Duplicate node id '${nodeId}' detected...`);
+ }
+ acc.set(nodeId, node);
+
+ if (node.children) {
+ for (const child of node.children) {
+ this.generateIdToNodeMapping(child, acc);
+ }
+ }
+ return acc;
+ }
+
+ private cloneDiffTreeNode(node: TraceTreeNode | null): TraceTreeNode | null {
+ const clone = ObjectFormatter.cloneObject(node);
+ if (node) {
+ clone.children = node.children;
+ clone.name = node.name;
+ clone.kind = node.kind;
+ clone.stableId = node.stableId;
+ clone.shortName = node.shortName;
+ }
+ return clone;
+ }
+
+ private makeTreeNode(node: TraceTreeNode): HierarchyTreeNode {
+ const clone = new HierarchyTreeNode(node.name, node.kind, node.stableId);
+ if (node.shortName) clone.shortName = node.shortName;
+ if (node.type) clone.type = node.type;
+ if (node.id) clone.id = node.id;
+ if (node.layerId) clone.layerId = node.layerId;
+ if (node.isVisible) clone.isVisible = node.isVisible;
+ if (node.isMissing) clone.isMissing = node.isMissing;
+ if (node.hwcCompositionType) clone.hwcCompositionType = node.hwcCompositionType;
+ if (node.zOrderRelativeOfId) clone.zOrderRelativeOfId = node.zOrderRelativeOfId;
+ if (node.isRootLayer) clone.isRootLayer = node.isRootLayer;
+ if (node.diffType) clone.diffType = node.diffType;
+ if (node.skip) clone.skip = node.skip;
+
+ return clone;
+ }
+
+ private generateDiffTree(
+ newTree: TraceTreeNode | null,
+ oldTree: TraceTreeNode | null,
+ newTreeSiblings: Array<TraceTreeNode | null>,
+ oldTreeSiblings: Array<TraceTreeNode | null>
+ ): TraceTreeNode[] {
+ const diffTrees = [];
+ // NOTE: A null ID represents a non existent node.
+ if (!this.getNodeId) {
+ return [];
+ }
+ 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) {
+ // Clone is required because trees are frozen objects — we can't modify the original tree object.
+ const diffTree = this.cloneDiffTreeNode(newTree)!;
+
+ // Default to no changes
+ diffTree.diffType = DiffType.NONE;
+
+ if (!UiTreeUtils.isParentNode(newTree.kind) && newId !== oldId) {
+ // A move, addition, or deletion has occurred
+ let nextOldTree: TraceTreeNode | null = null;
+
+ // Check if newTree has been added or moved
+ if (newId && !oldTreeSiblingIds.includes(newId)) {
+ if (this.oldMapping && this.oldMapping.get(newId)) {
+ // Objected existed in old tree, so DELETED_MOVE will be/has been flagged and added to the
+ // diffTree when visiting it in the oldTree.
+ diffTree.diffType = DiffType.ADDED_MOVE;
+
+ // Switch out oldTree for new one to compare against
+ nextOldTree = this.oldMapping.get(newId) ?? null;
+ } else {
+ diffTree.diffType = DiffType.ADDED;
+
+ // Stop comparing against oldTree
+ nextOldTree = null;
+ }
+ }
+
+ // Check if oldTree has been deleted of moved
+ if (oldId && oldTree && !newTreeSiblingIds.includes(oldId)) {
+ const deletedTreeDiff = this.cloneDiffTreeNode(oldTree)!;
+
+ if (this.newMapping && this.newMapping.get(oldId)) {
+ deletedTreeDiff.diffType = DiffType.DELETED_MOVE;
+
+ // Stop comparing against oldTree, will be/has been
+ // visited when object is seen in newTree
+ nextOldTree = null;
+ } else {
+ deletedTreeDiff.diffType = 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 {
+ if (this.isModified && this.isModified(newTree, oldTree)) {
+ diffTree.diffType = DiffType.MODIFIED;
+ }
+ }
+
+ diffTree.children = this.visitChildren(newTree, oldTree);
+ diffTrees.push(diffTree);
+ } else if (oldTree) {
+ if (oldId && !newTreeSiblingIds.includes(oldId)) {
+ // Deep clone oldTree omitting children field
+ const diffTree = this.cloneDiffTreeNode(oldTree)!;
+
+ // newTree doesn't exist, oldTree has either been moved or deleted.
+ if (this.newMapping && this.newMapping.get(oldId)) {
+ diffTree.diffType = DiffType.DELETED_MOVE;
+ } else {
+ diffTree.diffType = DiffType.DELETED;
+ }
+
+ diffTree.children = this.visitChildren(null, oldTree);
+ diffTrees.push(diffTree);
+ }
+ } else {
+ throw new Error('Both newTree and oldTree are undefined...');
+ }
+
+ return diffTrees;
+ }
+
+ private visitChildren(
+ newTree: TraceTreeNode | null,
+ oldTree: TraceTreeNode | null
+ ): TraceTreeNode[] {
+ // Recursively traverse all children of new and old tree.
+ const diffChildren = [];
+ const numOfChildren = Math.max(newTree?.children?.length ?? 0, oldTree?.children?.length ?? 0);
+ for (let i = 0; i < numOfChildren; i++) {
+ const newChild = newTree?.children ? newTree.children[i] : null;
+ const oldChild = oldTree?.children ? oldTree.children[i] : null;
+
+ const childDiffTrees = this.generateDiffTree(
+ newChild,
+ oldChild,
+ newTree?.children ?? [],
+ oldTree?.children ?? []
+ ).filter((tree) => tree != null);
+ diffChildren.push(...childDiffTrees);
+ }
+
+ return diffChildren;
+ }
+
+ private defaultNodeIdCallback(node: TraceTreeNode | null): string | null {
+ return node ? node.stableId : null;
+ }
+
+ private defaultModifiedCheck(
+ newNode: TraceTreeNode | null,
+ oldNode: TraceTreeNode | null
+ ): boolean {
+ if (!newNode && !oldNode) {
+ return false;
+ } else if (newNode && UiTreeUtils.isParentNode(newNode.kind)) {
+ return false;
+ } else if ((newNode && !oldNode) || (!newNode && oldNode)) {
+ return true;
+ } else if (newNode?.equals) {
+ return !newNode.equals(oldNode);
+ }
+ return false;
+ }
+}
diff --git a/tools/winscope/src/viewers/common/tree_generator_test.ts b/tools/winscope/src/viewers/common/tree_generator_test.ts
new file mode 100644
index 0000000..d66307f
--- /dev/null
+++ b/tools/winscope/src/viewers/common/tree_generator_test.ts
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 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 {TreeUtils} from 'common/tree_utils';
+import {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TreeGenerator} from 'viewers/common/tree_generator';
+import {DiffType, HierarchyTreeNode} from 'viewers/common/ui_tree_utils';
+
+describe('TreeGenerator', () => {
+ let entry: TraceTreeNode;
+
+ beforeAll(async () => {
+ entry = {
+ kind: 'entry',
+ name: 'LayerTraceEntry',
+ stableId: 'LayerTraceEntry',
+ id: 0,
+ parent: undefined,
+ children: [
+ {
+ kind: '3',
+ id: 3,
+ name: 'Child1',
+ stableId: '3 Child1',
+ parent: undefined,
+ children: [
+ {
+ kind: '2',
+ id: 2,
+ name: 'Child2',
+ stableId: '2 Child2',
+ parent: undefined,
+ children: [],
+ },
+ ],
+ },
+ ],
+ };
+ });
+
+ it('generates tree', () => {
+ const expected: HierarchyTreeNode = new HierarchyTreeBuilder()
+ .setName('LayerTraceEntry')
+ .setKind('entry')
+ .setStableId('LayerTraceEntry')
+ .setChildren([
+ new HierarchyTreeBuilder()
+ .setName('Child1')
+ .setStableId('3 Child1')
+ .setKind('3')
+ .setChildren([
+ new HierarchyTreeBuilder()
+ .setName('Child2')
+ .setStableId('2 Child2')
+ .setKind('2')
+ .setId(2)
+ .build(),
+ ])
+ .setId(3)
+ .build(),
+ ])
+ .setId(0)
+ .build();
+
+ const filter = TreeUtils.makeNodeFilter('');
+ const generator = new TreeGenerator(entry, filter);
+ expect(generator.generateTree()).toEqual(expected);
+ });
+
+ it('generates diff tree with no diff', () => {
+ const expected: HierarchyTreeNode = new HierarchyTreeBuilder()
+ .setName('LayerTraceEntry')
+ .setKind('entry')
+ .setStableId('LayerTraceEntry')
+ .setChildren([
+ new HierarchyTreeBuilder()
+ .setName('Child1')
+ .setStableId('3 Child1')
+ .setKind('3')
+ .setChildren([
+ new HierarchyTreeBuilder()
+ .setName('Child2')
+ .setStableId('2 Child2')
+ .setKind('2')
+ .setId(2)
+ .setDiffType(DiffType.NONE)
+ .build(),
+ ])
+ .setId(3)
+ .setDiffType(DiffType.NONE)
+ .build(),
+ ])
+ .setId(0)
+ .setDiffType(DiffType.NONE)
+ .build();
+
+ const filter = TreeUtils.makeNodeFilter('');
+ const tree = new TreeGenerator(entry, filter)
+ .withUniqueNodeId((node: any) => {
+ if (node) return node.stableId;
+ else return null;
+ })
+ .compareWith(entry)
+ .generateFinalTreeWithDiff();
+ expect(tree).toEqual(expected);
+ });
+
+ it('generates diff tree with moved node', () => {
+ const prevEntry: TraceTreeNode = {
+ kind: 'entry',
+ name: 'LayerTraceEntry',
+ stableId: 'LayerTraceEntry',
+ id: 0,
+ parent: undefined,
+ children: [
+ {
+ kind: '3',
+ id: 3,
+ stableId: '3 Child1',
+ name: 'Child1',
+ parent: undefined,
+ children: [],
+ },
+ {
+ kind: '2',
+ id: 2,
+ stableId: '2 Child2',
+ name: 'Child2',
+ parent: undefined,
+ children: [],
+ },
+ ],
+ };
+
+ const expected: HierarchyTreeNode = new HierarchyTreeBuilder()
+ .setName('LayerTraceEntry')
+ .setKind('entry')
+ .setStableId('LayerTraceEntry')
+ .setChildren([
+ new HierarchyTreeBuilder()
+ .setName('Child1')
+ .setStableId('3 Child1')
+ .setKind('3')
+ .setChildren([
+ new HierarchyTreeBuilder()
+ .setName('Child2')
+ .setStableId('2 Child2')
+ .setKind('2')
+ .setId(2)
+ .setDiffType(DiffType.ADDED_MOVE)
+ .build(),
+ ])
+ .setId(3)
+ .setDiffType(DiffType.NONE)
+ .build(),
+ new HierarchyTreeBuilder()
+ .setName('Child2')
+ .setStableId('2 Child2')
+ .setKind('2')
+ .setId(2)
+ .setDiffType(DiffType.DELETED_MOVE)
+ .build(),
+ ])
+ .setId(0)
+ .setDiffType(DiffType.NONE)
+ .build();
+
+ const filter = TreeUtils.makeNodeFilter('');
+ const generator = new TreeGenerator(entry, filter);
+ const newDiffTree = generator
+ .withUniqueNodeId((node: any) => {
+ if (node) return node.stableId;
+ else return null;
+ })
+ .compareWith(prevEntry)
+ .generateFinalTreeWithDiff();
+
+ expect(newDiffTree).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/viewers/common/tree_transformer.ts b/tools/winscope/src/viewers/common/tree_transformer.ts
new file mode 100644
index 0000000..a39eb3e
--- /dev/null
+++ b/tools/winscope/src/viewers/common/tree_transformer.ts
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2022 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 {FilterType, TreeNode} from 'common/tree_utils';
+import {ObjectFormatter} from 'trace/flickerlib/ObjectFormatter';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+
+import {
+ DiffType,
+ HierarchyTreeNode,
+ PropertiesDump,
+ PropertiesTreeNode,
+ Terminal,
+} from './ui_tree_utils';
+
+interface TransformOptions {
+ freeze: boolean;
+ keepOriginal: boolean;
+ metadataKey: string | null;
+}
+interface TreeTransformerOptions {
+ skip?: any;
+ formatter?: any;
+}
+
+export class TreeTransformer {
+ private stableId: string;
+ private rootName: string;
+ private isShowDefaults = false;
+ private isShowDiff = false;
+ private filter: FilterType;
+ private properties: PropertiesDump | Terminal | null = null;
+ private compareWithProperties: PropertiesDump | Terminal | null = null;
+ private options?: TreeTransformerOptions;
+ private onlyProtoDump = false;
+ private transformOptions: TransformOptions = {
+ keepOriginal: false,
+ freeze: true,
+ metadataKey: null,
+ };
+
+ constructor(selectedTree: HierarchyTreeNode, filter: FilterType) {
+ this.stableId = this.compatibleStableId(selectedTree);
+ this.rootName = selectedTree.name;
+ this.filter = filter;
+ this.setTransformerOptions({});
+ }
+
+ setOnlyProtoDump(onlyProto: boolean): TreeTransformer {
+ this.onlyProtoDump = onlyProto;
+ return this;
+ }
+
+ setIsShowDefaults(enabled: boolean): TreeTransformer {
+ this.isShowDefaults = enabled;
+ return this;
+ }
+
+ setIsShowDiff(enabled: boolean): TreeTransformer {
+ this.isShowDiff = enabled;
+ return this;
+ }
+
+ setTransformerOptions(options: TreeTransformerOptions): TreeTransformer {
+ this.options = options;
+ if (!this.options.formatter) {
+ this.options.formatter = this.formatProto;
+ }
+ return this;
+ }
+
+ setProperties(currentEntry: TraceTreeNode | null): TreeTransformer {
+ const currFlickerItem = this.getOriginalFlickerItem(currentEntry, this.stableId);
+ const target = currFlickerItem ? currFlickerItem.obj ?? currFlickerItem : null;
+ ObjectFormatter.displayDefaults = this.isShowDefaults;
+ this.properties = this.onlyProtoDump
+ ? this.getProtoDumpPropertiesForDisplay(target)
+ : this.getPropertiesForDisplay(target);
+ return this;
+ }
+
+ setDiffProperties(previousEntry: TraceTreeNode | null): TreeTransformer {
+ if (this.isShowDiff) {
+ const prevFlickerItem = this.findFlickerItem(previousEntry, this.stableId);
+ const target = prevFlickerItem ? prevFlickerItem.obj ?? prevFlickerItem : null;
+ this.compareWithProperties = this.onlyProtoDump
+ ? this.getProtoDumpPropertiesForDisplay(target)
+ : this.getPropertiesForDisplay(target);
+ }
+ return this;
+ }
+
+ getOriginalFlickerItem(entry: TraceTreeNode | null, stableId: string): TraceTreeNode | null {
+ return this.findFlickerItem(entry, stableId);
+ }
+
+ private getProtoDumpPropertiesForDisplay(entry: TraceTreeNode): PropertiesDump | null {
+ if (!entry) {
+ return null;
+ }
+
+ return ObjectFormatter.format(entry.proto);
+ }
+
+ private getPropertiesForDisplay(entry: TraceTreeNode): PropertiesDump | null {
+ if (!entry) {
+ return null;
+ }
+
+ return ObjectFormatter.format(entry);
+ }
+
+ private findFlickerItem(
+ entryFlickerItem: TraceTreeNode | null,
+ stableId: string
+ ): TraceTreeNode | null {
+ if (!entryFlickerItem) {
+ return null;
+ }
+
+ if (entryFlickerItem.stableId && entryFlickerItem.stableId === stableId) {
+ return entryFlickerItem;
+ }
+
+ if (!entryFlickerItem.children) {
+ return null;
+ }
+
+ for (const child of entryFlickerItem.children) {
+ const foundEntry: any = this.findFlickerItem(child, stableId);
+ if (foundEntry) {
+ return foundEntry;
+ }
+ }
+
+ return null;
+ }
+
+ transform(): PropertiesTreeNode {
+ const {formatter} = this.options!;
+ if (!formatter) {
+ throw new Error('Missing formatter, please set with setOptions()');
+ }
+
+ const transformedTree = this.transformTree(
+ this.properties,
+ this.rootName,
+ this.compareWithProperties,
+ this.rootName,
+ this.stableId,
+ this.transformOptions
+ );
+ return transformedTree;
+ }
+
+ private transformTree(
+ properties: PropertiesDump | null | Terminal,
+ name: string | Terminal,
+ compareWithProperties: PropertiesDump | null | Terminal,
+ compareWithName: string | Terminal,
+ stableId: string,
+ transformOptions: TransformOptions
+ ): PropertiesTreeNode {
+ const originalProperties = properties;
+ const metadata = this.getMetadata(originalProperties, transformOptions.metadataKey);
+
+ const children: any[] = [];
+
+ if (properties === null) {
+ properties = 'null';
+ }
+
+ if (!this.isTerminal(properties)) {
+ const transformedProperties = this.transformProperties(
+ properties,
+ transformOptions.metadataKey
+ );
+ properties = transformedProperties.properties;
+ }
+
+ if (compareWithProperties && !this.isTerminal(compareWithProperties)) {
+ const transformedProperties = this.transformProperties(
+ compareWithProperties,
+ transformOptions.metadataKey
+ );
+ compareWithProperties = transformedProperties.properties;
+ }
+
+ for (const key in properties) {
+ if (!(properties instanceof Terminal) /* && properties[key]*/) {
+ let compareWithChild = new Terminal();
+ let compareWithChildName = new Terminal();
+ if (
+ compareWithProperties &&
+ !(compareWithProperties instanceof Terminal) &&
+ compareWithProperties[key]
+ ) {
+ compareWithChild = compareWithProperties[key];
+ compareWithChildName = key;
+ }
+ const child = this.transformTree(
+ properties[key],
+ key,
+ compareWithChild,
+ compareWithChildName,
+ `${stableId}.${key}`,
+ transformOptions
+ );
+
+ children.push(child);
+ }
+ }
+
+ // Takes care of adding deleted items to final tree
+ for (const key in compareWithProperties) {
+ if (
+ properties &&
+ !(properties instanceof Terminal) &&
+ !properties[key] &&
+ !(compareWithProperties instanceof Terminal) &&
+ compareWithProperties[key]
+ ) {
+ const child = this.transformTree(
+ new Terminal(),
+ new Terminal(),
+ compareWithProperties[key],
+ key,
+ `${stableId}.${key}`,
+ transformOptions
+ );
+
+ children.push(child);
+ }
+ }
+
+ let transformedProperties: any;
+ if (children.length === 1 && children[0].children?.length === 0 && !children[0].combined) {
+ // Merge leaf key value pairs.
+ const child = children[0];
+
+ transformedProperties = {
+ kind: '',
+ name: (this.isTerminal(name) ? compareWithName : name) + ': ' + child.name,
+ stableId,
+ children: child.children,
+ combined: true,
+ };
+
+ if (this.isShowDiff) {
+ transformedProperties.diffType = child.diffType;
+ }
+ } else {
+ transformedProperties = {
+ kind: '',
+ name,
+ stableId,
+ children,
+ };
+
+ if (this.isShowDiff) {
+ const diffType = this.getDiff(name, compareWithName);
+ transformedProperties.diffType = diffType;
+
+ if (diffType === DiffType.DELETED) {
+ transformedProperties.name = compareWithName;
+ }
+ }
+ }
+
+ if (transformOptions.keepOriginal) {
+ transformedProperties.properties = originalProperties;
+ }
+
+ if (metadata && transformOptions.metadataKey) {
+ transformedProperties[transformOptions.metadataKey] = metadata;
+ }
+
+ if (!this.isTerminal(transformedProperties.name)) {
+ transformedProperties.propertyKey = this.getPropertyKey(transformedProperties);
+ transformedProperties.propertyValue = this.getPropertyValue(transformedProperties);
+ }
+
+ if (
+ !this.filterMatches(transformedProperties) &&
+ !this.hasChildMatchingFilter(transformedProperties?.children)
+ ) {
+ transformedProperties.propertyKey = new Terminal();
+ }
+ return transformOptions.freeze ? Object.freeze(transformedProperties) : transformedProperties;
+ }
+
+ private hasChildMatchingFilter(children: PropertiesTreeNode[] | null | undefined): boolean {
+ if (!children || children.length === 0) return false;
+
+ let match = false;
+ for (let i = 0; i < children.length; i++) {
+ if (this.filterMatches(children[i]) || this.hasChildMatchingFilter(children[i].children)) {
+ match = true;
+ }
+ }
+
+ return match;
+ }
+
+ private getMetadata(obj: PropertiesDump | null | Terminal, metadataKey: string | null): any {
+ if (obj == null) {
+ return null;
+ }
+ if (metadataKey && !(obj instanceof Terminal) && obj[metadataKey]) {
+ const metadata = obj[metadataKey];
+ obj[metadataKey] = undefined;
+ return metadata;
+ } else {
+ return null;
+ }
+ }
+
+ private getPropertyKey(item: PropertiesDump): string {
+ if (item['name'] && (!item['children'] || item['children'].length === 0)) {
+ return item['name'].split(': ')[0];
+ }
+ return item['name'];
+ }
+
+ private getPropertyValue(item: PropertiesDump): string | null {
+ if (item['name'] && (!item['children'] || item['children'].length === 0)) {
+ return item['name'].split(': ').slice(1).join(': ');
+ }
+ return null;
+ }
+
+ private filterMatches(item: PropertiesDump | null): boolean {
+ //TODO: fix PropertiesDump type. What is it? Why does it declare only a "key" property and yet it is used as a TreeNode?
+ return this.filter(item as TreeNode) ?? false;
+ }
+
+ private transformProperties(
+ properties: PropertiesDump,
+ metadataKey: string | null
+ ): PropertiesTreeNode {
+ const {skip, formatter} = this.options!;
+ const transformedProperties: PropertiesTreeNode = {
+ properties: {},
+ };
+
+ if (skip && skip.includes(properties)) {
+ return transformedProperties; // skip
+ }
+
+ const formatted = formatter(properties);
+ if (formatted) {
+ // Obj has been formatted into a terminal node — has no children.
+ transformedProperties.properties[formatted] = new Terminal();
+ } else if (Array.isArray(properties)) {
+ properties.forEach((e, i) => {
+ transformedProperties.properties['' + i] = e;
+ });
+ } else if (typeof properties === 'string') {
+ // Object is a primitive type — has no children. Set to terminal
+ // to differentiate between null object and Terminal element.
+ transformedProperties.properties[properties] = new Terminal();
+ } else if (typeof properties === 'number' || typeof properties === 'boolean') {
+ // Similar to above — primitive type node has no children.
+ transformedProperties.properties['' + properties] = new Terminal();
+ } else if (properties && typeof properties === 'object') {
+ // Empty objects
+ if (Object.keys(properties).length === 0) {
+ transformedProperties.properties['[empty]'] = new Terminal();
+ } else {
+ // Non empty objects
+ Object.keys(properties).forEach((key) => {
+ if (key === metadataKey) {
+ return;
+ }
+ transformedProperties.properties[key] = properties[key];
+ });
+ }
+ } else if (properties === null) {
+ // Null object has no children — set to be terminal node.
+ transformedProperties.properties.null = new Terminal();
+ }
+ return transformedProperties;
+ }
+
+ private getDiff(val: string | Terminal, compareVal: string | Terminal): string {
+ if (val && this.isTerminal(compareVal)) {
+ return DiffType.ADDED;
+ } else if (this.isTerminal(val) && compareVal) {
+ return DiffType.DELETED;
+ } else if (compareVal !== val) {
+ return DiffType.MODIFIED;
+ } else {
+ return DiffType.NONE;
+ }
+ }
+
+ private compatibleStableId(item: HierarchyTreeNode): string {
+ // 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;
+ }
+
+ private formatProto(item: any) {
+ if (item?.prettyPrint) {
+ return item.prettyPrint();
+ }
+ }
+
+ private isTerminal(item: any): boolean {
+ return item instanceof Terminal;
+ }
+}
diff --git a/tools/winscope/src/viewers/common/tree_transformer_test.ts b/tools/winscope/src/viewers/common/tree_transformer_test.ts
new file mode 100644
index 0000000..03d1a25
--- /dev/null
+++ b/tools/winscope/src/viewers/common/tree_transformer_test.ts
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 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 {TreeUtils} from 'common/tree_utils';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TreeTransformer} from 'viewers/common/tree_transformer';
+import {DiffType, HierarchyTreeNode} from 'viewers/common/ui_tree_utils';
+
+describe('TreeTransformer', () => {
+ let entry: TraceTreeNode;
+ let selectedTree: HierarchyTreeNode;
+ beforeAll(async () => {
+ entry = {
+ id: 3,
+ name: 'Child1',
+ stackId: 0,
+ isVisible: true,
+ kind: '3',
+ stableId: '3 Child1',
+ proto: {
+ barrierLayer: [],
+ id: 3,
+ parent: 1,
+ type: 'ContainerLayer',
+ },
+ parent: undefined,
+ children: [
+ {
+ id: 2,
+ name: 'Child2',
+ stackId: 0,
+ parent: undefined,
+ children: [],
+ kind: '2',
+ stableId: '2 Child2',
+ proto: {
+ barrierLayer: [],
+ id: 2,
+ parent: 3,
+ type: 'ContainerLayer',
+ },
+ isVisible: true,
+ },
+ ],
+ };
+
+ selectedTree = {
+ id: 3,
+ name: 'Child1',
+ stackId: 0,
+ isVisible: true,
+ kind: '3',
+ stableId: '3 Child1',
+ showInFilteredView: true,
+ skip: null,
+ chips: [],
+ children: [
+ {
+ id: 2,
+ name: 'Child2',
+ stackId: 0,
+ children: [],
+ kind: '2',
+ stableId: '2 Child2',
+ isVisible: true,
+ showInFilteredView: true,
+ chips: [],
+ },
+ ],
+ };
+ });
+
+ it('creates ordinary properties tree without show diff enabled', () => {
+ const expected = {
+ kind: '',
+ name: 'Child1',
+ stableId: '3 Child1',
+ children: [
+ {
+ kind: '',
+ name: 'id: 3',
+ stableId: '3 Child1.id',
+ children: [],
+ combined: true,
+ propertyKey: 'id',
+ propertyValue: '3',
+ },
+ {
+ kind: '',
+ name: 'type: ContainerLayer',
+ stableId: '3 Child1.type',
+ children: [],
+ combined: true,
+ propertyKey: 'type',
+ propertyValue: 'ContainerLayer',
+ },
+ ],
+ propertyKey: 'Child1',
+ propertyValue: null,
+ };
+
+ const filter = TreeUtils.makeNodeFilter('');
+ const transformer = new TreeTransformer(selectedTree, filter)
+ .setOnlyProtoDump(true)
+ .setProperties(entry);
+
+ const transformedTree = transformer.transform();
+ expect(transformedTree).toEqual(expected);
+ });
+
+ it('creates properties tree with show diff enabled, comparing to a null previous entry', () => {
+ const expected = {
+ kind: '',
+ name: 'Child1',
+ stableId: '3 Child1',
+ children: [
+ {
+ kind: '',
+ name: 'id: 3',
+ diffType: DiffType.ADDED,
+ stableId: '3 Child1.id',
+ children: [],
+ combined: true,
+ propertyKey: 'id',
+ propertyValue: '3',
+ },
+ {
+ kind: '',
+ name: 'type: ContainerLayer',
+ diffType: DiffType.ADDED,
+ stableId: '3 Child1.type',
+ children: [],
+ combined: true,
+ propertyKey: 'type',
+ propertyValue: 'ContainerLayer',
+ },
+ ],
+ diffType: DiffType.NONE,
+ propertyKey: 'Child1',
+ propertyValue: null,
+ };
+
+ const filter = TreeUtils.makeNodeFilter('');
+ const transformer = new TreeTransformer(selectedTree, filter)
+ .setIsShowDiff(true)
+ .setOnlyProtoDump(true)
+ .setProperties(entry)
+ .setDiffProperties(null);
+
+ const transformedTree = transformer.transform();
+ expect(transformedTree).toEqual(expected);
+ });
+});
diff --git a/tools/winscope/src/viewers/common/ui_tree_utils.ts b/tools/winscope/src/viewers/common/ui_tree_utils.ts
new file mode 100644
index 0000000..0039e71
--- /dev/null
+++ b/tools/winscope/src/viewers/common/ui_tree_utils.ts
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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';
+
+export type UiTreeNode = HierarchyTreeNode | PropertiesTreeNode;
+
+export class HierarchyTreeNode {
+ constructor(
+ public name: string,
+ public kind: string,
+ public stableId: string,
+ children?: HierarchyTreeNode[]
+ ) {
+ this.children = children ?? [];
+ }
+
+ children: HierarchyTreeNode[];
+ shortName?: string;
+ type?: string;
+ id?: string | number;
+ layerId?: number;
+ displayId?: number;
+ stackId?: number;
+ isVisible?: boolean;
+ isMissing?: boolean;
+ hwcCompositionType?: number;
+ zOrderRelativeOfId?: number;
+ zOrderRelativeOf?: any;
+ zOrderRelativeParentOf?: any;
+ isRootLayer?: boolean;
+ showInFilteredView?: boolean;
+ showInOnlyVisibleView?: boolean;
+ simplifyNames?: boolean;
+ chips: Chip[] = [];
+ diffType?: string;
+ skip?: any;
+}
+
+export interface PropertiesDump {
+ [key: string]: any;
+}
+
+export interface PropertiesTreeNode {
+ properties?: any;
+ kind?: string;
+ stableId?: string;
+ children?: PropertiesTreeNode[];
+ propertyKey?: string | Terminal | null;
+ propertyValue?: string | Terminal | null;
+ name?: string | Terminal;
+ diffType?: string;
+ combined?: boolean;
+} //TODO: make specific
+
+export const DiffType = {
+ NONE: 'none',
+ ADDED: 'added',
+ DELETED: 'deleted',
+ ADDED_MOVE: 'addedMove',
+ DELETED_MOVE: 'deletedMove',
+ MODIFIED: 'modified',
+};
+
+export class Terminal {}
+
+export class UiTreeUtils {
+ static diffClass(item: UiTreeNode): string {
+ const diffType = item.diffType;
+ return diffType ?? '';
+ }
+
+ static isHighlighted(item: UiTreeNode, highlightedItems: string[]) {
+ return item instanceof HierarchyTreeNode && highlightedItems.includes(`${item.stableId}`);
+ }
+
+ static isVisibleNode(kind: string, type?: string) {
+ return kind === 'WindowState' || kind === 'Activity' || type?.includes('Layer');
+ }
+
+ static isParentNode(kind: string) {
+ return UiTreeUtils.PARENT_NODE_KINDS.includes(kind);
+ }
+
+ private static readonly PARENT_NODE_KINDS = [
+ 'entry',
+ 'WindowManagerState',
+ 'InputMethodClient entry',
+ 'InputMethodService entry',
+ 'InputMethodManagerService entry',
+ ];
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/viewers/common/user_options.ts
similarity index 75%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/viewers/common/user_options.ts
index bfe19ce..d5c2bdb 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/viewers/common/user_options.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-
-package org.chromium.arc.wayland_composer;
-
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export interface UserOptions {
+ [key: string]: {
+ name: string;
+ enabled: boolean;
+ tooltip?: string;
+ };
+}
diff --git a/tools/winscope/src/viewers/common/viewer_events.ts b/tools/winscope/src/viewers/common/viewer_events.ts
new file mode 100644
index 0000000..fd109da
--- /dev/null
+++ b/tools/winscope/src/viewers/common/viewer_events.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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 const ViewerEvents = {
+ HierarchyPinnedChange: 'HierarchyPinnedChange',
+ HighlightedChange: 'HighlightedChange',
+ HierarchyUserOptionsChange: 'HierarchyUserOptionsChange',
+ HierarchyFilterChange: 'HierarchyFilterChange',
+ SelectedTreeChange: 'SelectedTreeChange',
+ PropertiesUserOptionsChange: 'PropertiesUserOptionsChange',
+ PropertiesFilterChange: 'PropertiesFilterChange',
+ AdditionalPropertySelected: 'AdditionalPropertySelected',
+};
diff --git a/tools/winscope/src/viewers/common/viewer_input_method.ts b/tools/winscope/src/viewers/common/viewer_input_method.ts
new file mode 100644
index 0000000..81d579f
--- /dev/null
+++ b/tools/winscope/src/viewers/common/viewer_input_method.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {ImeUiData} from 'viewers/common/ime_ui_data';
+import {PresenterInputMethod} from 'viewers/common/presenter_input_method';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+import {View, Viewer} from 'viewers/viewer';
+
+abstract class ViewerInputMethod implements Viewer {
+ constructor(traces: Traces, storage: Storage) {
+ this.htmlElement = document.createElement('viewer-input-method');
+ this.presenter = this.initialisePresenter(traces, storage);
+ this.addViewerEventListeners();
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.presenter.onTracePositionUpdate(position);
+ }
+
+ abstract getViews(): View[];
+ abstract getDependencies(): TraceType[];
+
+ protected imeUiCallback = (uiData: ImeUiData) => {
+ // Angular does not deep watch @Input properties. Clearing inputData to null before repopulating
+ // automatically ensures that the UI will change via the Angular change detection cycle. Without
+ // resetting, Angular does not auto-detect that inputData has changed.
+ (this.htmlElement as any).inputData = null;
+ (this.htmlElement as any).inputData = uiData;
+ };
+
+ protected addViewerEventListeners() {
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) =>
+ this.presenter.updatePinnedItems((event as CustomEvent).detail.pinnedItem)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HighlightedChange, (event) =>
+ this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) =>
+ this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyFilterChange, (event) =>
+ this.presenter.filterHierarchyTree((event as CustomEvent).detail.filterString)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.PropertiesUserOptionsChange, (event) =>
+ this.presenter.updatePropertiesTree((event as CustomEvent).detail.userOptions)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.PropertiesFilterChange, (event) =>
+ this.presenter.filterPropertiesTree((event as CustomEvent).detail.filterString)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.SelectedTreeChange, (event) =>
+ this.presenter.newPropertiesTree((event as CustomEvent).detail.selectedItem)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.AdditionalPropertySelected, (event) =>
+ this.presenter.newAdditionalPropertiesTree((event as CustomEvent).detail.selectedItem)
+ );
+ }
+
+ protected abstract initialisePresenter(traces: Traces, storage: Storage): PresenterInputMethod;
+
+ protected htmlElement: HTMLElement;
+ protected presenter: PresenterInputMethod;
+}
+
+export {ViewerInputMethod};
diff --git a/tools/winscope/src/viewers/components/coordinates_table_component.ts b/tools/winscope/src/viewers/components/coordinates_table_component.ts
new file mode 100644
index 0000000..004c112
--- /dev/null
+++ b/tools/winscope/src/viewers/components/coordinates_table_component.ts
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+
+@Component({
+ selector: 'coordinates-table',
+ template: `
+ <p *ngIf="!hasCoordinates()" class="mat-body-1">null</p>
+ <table *ngIf="hasCoordinates()" class="table">
+ <tr class="header-row">
+ <td>
+ <p class="mat-body-1">Left</p>
+ </td>
+ <td>
+ <p class="mat-body-1">Top</p>
+ </td>
+ <td>
+ <p class="mat-body-1">Right</p>
+ </td>
+ <td>
+ <p class="mat-body-1">Bottom</p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p class="mat-body-1">{{ coordinates.left }}</p>
+ </td>
+ <td>
+ <p class="mat-body-1">{{ coordinates.top }}</p>
+ </td>
+ <td>
+ <p class="mat-body-1">{{ coordinates.right }}</p>
+ </td>
+ <td>
+ <p class="mat-body-1">{{ coordinates.bottom }}</p>
+ </td>
+ </tr>
+ </table>
+ `,
+ styles: [
+ `
+ .table {
+ width: 100%;
+ border-collapse: collapse;
+ }
+
+ .table td {
+ padding: 1px 5px;
+ border: 1px solid var(--border-color);
+ text-align: center;
+ overflow-wrap: anywhere;
+ }
+
+ .header-row td {
+ color: gray;
+ }
+ `,
+ ],
+})
+export class CoordinatesTableComponent {
+ @Input() coordinates!: any;
+
+ hasCoordinates() {
+ return (
+ this.coordinates.left ||
+ this.coordinates.top ||
+ this.coordinates.right ||
+ this.coordinates.bottom
+ );
+ }
+}
diff --git a/tools/winscope/src/viewers/components/coordinates_table_component_test.ts b/tools/winscope/src/viewers/components/coordinates_table_component_test.ts
new file mode 100644
index 0000000..d445d6a
--- /dev/null
+++ b/tools/winscope/src/viewers/components/coordinates_table_component_test.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {CoordinatesTableComponent} from './coordinates_table_component';
+
+describe('CoordinatesTableComponent', () => {
+ let fixture: ComponentFixture<CoordinatesTableComponent>;
+ let component: CoordinatesTableComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [CoordinatesTableComponent],
+ schemas: [],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CoordinatesTableComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/hierarchy_component.ts b/tools/winscope/src/viewers/components/hierarchy_component.ts
new file mode 100644
index 0000000..dd4ca94
--- /dev/null
+++ b/tools/winscope/src/viewers/components/hierarchy_component.ts
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 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 {Component, ElementRef, Inject, Input} from '@angular/core';
+import {PersistentStore} from 'common/persistent_store';
+import {TraceType} from 'trace/trace_type';
+import {TableProperties} from 'viewers/common/table_properties';
+import {HierarchyTreeNode, UiTreeNode, UiTreeUtils} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+import {nodeStyles} from 'viewers/components/styles/node.styles';
+
+@Component({
+ selector: 'hierarchy-view',
+ template: `
+ <div class="view-header">
+ <div class="title-filter">
+ <h2 class="hierarchy-title mat-title">Hierarchy</h2>
+ <mat-form-field>
+ <mat-label>Filter...</mat-label>
+ <input matInput [(ngModel)]="filterString" (ngModelChange)="filterTree()" name="filter" />
+ </mat-form-field>
+ </div>
+ <div class="view-controls">
+ <mat-checkbox
+ *ngFor="let option of objectKeys(userOptions)"
+ color="primary"
+ [(ngModel)]="userOptions[option].enabled"
+ (ngModelChange)="updateTree()"
+ >{{ userOptions[option].name }}</mat-checkbox
+ >
+ </div>
+ <properties-table
+ *ngIf="tableProperties"
+ class="properties-table"
+ [properties]="tableProperties"></properties-table>
+ <div *ngIf="pinnedItems.length > 0" class="pinned-items">
+ <tree-node
+ *ngFor="let pinnedItem of pinnedItems"
+ class="node"
+ [class]="diffClass(pinnedItem)"
+ [class.selected]="isHighlighted(pinnedItem, highlightedItems)"
+ [class.clickable]="true"
+ [item]="pinnedItem"
+ [isPinned]="true"
+ [isInPinnedSection]="true"
+ (pinNodeChange)="pinnedItemChange($event)"
+ (click)="onPinnedNodeClick($event, pinnedItem)"></tree-node>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="hierarchy-content">
+ <tree-view
+ *ngIf="tree"
+ [isFlattened]="isFlattened()"
+ [item]="tree"
+ [dependencies]="dependencies"
+ [store]="store"
+ [useGlobalCollapsedState]="true"
+ [itemsClickable]="true"
+ [highlightedItems]="highlightedItems"
+ [pinnedItems]="pinnedItems"
+ (highlightedItemChange)="highlightedItemChange($event)"
+ (pinnedItemChange)="pinnedItemChange($event)"
+ (selectedTreeChange)="selectedTreeChange($event)"></tree-view>
+ </div>
+ `,
+ styles: [
+ `
+ .view-header {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 12px;
+ }
+
+ .title-filter {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ }
+
+ .view-controls {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ column-gap: 10px;
+ }
+
+ .properties-table {
+ padding-top: 5px;
+ }
+
+ .hierarchy-content {
+ height: 100%;
+ overflow: auto;
+ }
+
+ .pinned-items {
+ width: 100%;
+ box-sizing: border-box;
+ border: 2px solid yellow;
+ }
+
+ tree-view {
+ overflow: auto;
+ }
+ `,
+ nodeStyles,
+ ],
+})
+export class HierarchyComponent {
+ objectKeys = Object.keys;
+ filterString = '';
+ diffClass = UiTreeUtils.diffClass;
+ isHighlighted = UiTreeUtils.isHighlighted;
+
+ @Input() tree!: HierarchyTreeNode | null;
+ @Input() tableProperties?: TableProperties | null;
+ @Input() dependencies: TraceType[] = [];
+ @Input() highlightedItems: string[] = [];
+ @Input() pinnedItems: HierarchyTreeNode[] = [];
+ @Input() store!: PersistentStore;
+ @Input() userOptions: UserOptions = {};
+
+ constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
+
+ isFlattened() {
+ return this.userOptions['flat']?.enabled;
+ }
+
+ onPinnedNodeClick(event: MouseEvent, pinnedItem: HierarchyTreeNode) {
+ event.preventDefault();
+ if (window.getSelection()?.type === 'range') {
+ return;
+ }
+ if (pinnedItem.id) this.highlightedItemChange(`${pinnedItem.id}`);
+ this.selectedTreeChange(pinnedItem);
+ }
+
+ updateTree() {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.HierarchyUserOptionsChange, {
+ bubbles: true,
+ detail: {userOptions: this.userOptions},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ filterTree() {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.HierarchyFilterChange, {
+ bubbles: true,
+ detail: {filterString: this.filterString},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ highlightedItemChange(newId: string) {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.HighlightedChange, {
+ bubbles: true,
+ detail: {id: newId},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ selectedTreeChange(item: UiTreeNode) {
+ if (!(item instanceof HierarchyTreeNode)) {
+ return;
+ }
+ const event: CustomEvent = new CustomEvent(ViewerEvents.SelectedTreeChange, {
+ bubbles: true,
+ detail: {selectedItem: item},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ pinnedItemChange(item: UiTreeNode) {
+ if (!(item instanceof HierarchyTreeNode)) {
+ return;
+ }
+ const event: CustomEvent = new CustomEvent(ViewerEvents.HierarchyPinnedChange, {
+ bubbles: true,
+ detail: {pinnedItem: item},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+}
diff --git a/tools/winscope/src/viewers/components/hierarchy_component_test.ts b/tools/winscope/src/viewers/components/hierarchy_component_test.ts
new file mode 100644
index 0000000..33b8417
--- /dev/null
+++ b/tools/winscope/src/viewers/components/hierarchy_component_test.ts
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatInputModule} from '@angular/material/input';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {PersistentStore} from 'common/persistent_store';
+import {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {TreeComponent} from 'viewers/components/tree_component';
+import {TreeNodeComponent} from 'viewers/components/tree_node_component';
+import {TreeNodeDataViewComponent} from 'viewers/components/tree_node_data_view_component';
+import {HierarchyComponent} from './hierarchy_component';
+
+describe('HierarchyComponent', () => {
+ let fixture: ComponentFixture<HierarchyComponent>;
+ let component: HierarchyComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [
+ HierarchyComponent,
+ TreeComponent,
+ TreeNodeComponent,
+ TreeNodeDataViewComponent,
+ ],
+ imports: [
+ CommonModule,
+ MatCheckboxModule,
+ MatDividerModule,
+ MatInputModule,
+ MatFormFieldModule,
+ BrowserAnimationsModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(HierarchyComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+
+ component.tree = new HierarchyTreeBuilder()
+ .setName('Root node')
+ .setChildren([new HierarchyTreeBuilder().setName('Child node').build()])
+ .build();
+
+ component.store = new PersistentStore();
+ component.userOptions = {
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ };
+ component.pinnedItems = [component.tree];
+
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('renders title', () => {
+ const title = htmlElement.querySelector('.hierarchy-title');
+ expect(title).toBeTruthy();
+ });
+
+ it('renders view controls', () => {
+ const viewControls = htmlElement.querySelector('.view-controls');
+ expect(viewControls).toBeTruthy();
+ });
+
+ it('renders initial tree elements', async () => {
+ const treeView = htmlElement.querySelector('tree-view');
+ expect(treeView).toBeTruthy();
+ expect(treeView!.innerHTML).toContain('Root node');
+ expect(treeView!.innerHTML).toContain('Child node');
+ });
+});
diff --git a/tools/winscope/src/viewers/components/ime_additional_properties_component.ts b/tools/winscope/src/viewers/components/ime_additional_properties_component.ts
new file mode 100644
index 0000000..8b63ad5
--- /dev/null
+++ b/tools/winscope/src/viewers/components/ime_additional_properties_component.ts
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2022 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 {Component, ElementRef, Inject, Input} from '@angular/core';
+import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties';
+import {UiTreeUtils} from 'viewers/common/ui_tree_utils';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+
+@Component({
+ selector: 'ime-additional-properties',
+ template: `
+ <h2 class="view-header mat-title">WM & SF Properties</h2>
+ <div class="additional-properties-content">
+ <div *ngIf="isAllPropertiesNull()" class="group">
+ <p class="mat-body-1">
+ There is no corresponding WM / SF additionalProperties for this IME entry – no WM / SF
+ entry is recorded before this IME entry in time. View later frames for WM & SF properties.
+ </p>
+ </div>
+
+ <ng-container *ngIf="isImeManagerService">
+ <div class="group">
+ <button
+ *ngIf="wmProtoOrNull()"
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(wmProtoOrNull())}"
+ (click)="onClickShowInPropertiesPanel(wmProtoOrNull(), additionalProperties.wm?.name)">
+ WMState
+ </button>
+ <h3 *ngIf="!wmProtoOrNull()" class="group-header mat-subheading-2">WMState</h3>
+ <div class="left-column">
+ <p *ngIf="additionalProperties.wm" class="mat-body-1">
+ {{ additionalProperties.wm.name }}
+ </p>
+ <p *ngIf="!additionalProperties.wm" class="mat-body-1">
+ There is no corresponding WMState entry.
+ </p>
+ </div>
+ </div>
+ <div *ngIf="wmInsetsSourceProviderOrNull()" class="group">
+ <button
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(wmInsetsSourceProviderOrNull())}"
+ (click)="
+ onClickShowInPropertiesPanel(
+ wmInsetsSourceProviderOrNull(),
+ 'Ime Insets Source Provider'
+ )
+ ">
+ IME Insets Source Provider
+ </button>
+ <div class="left-column">
+ <p class="mat-body-2">Source Frame:</p>
+ <coordinates-table
+ [coordinates]="wmInsetsSourceProviderSourceFrameOrNull()"></coordinates-table>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Source Visible:</span>
+ &ngsp;
+ {{ wmInsetsSourceProviderSourceVisibleOrNull() }}
+ </p>
+ <p class="mat-body-2">Source Visible Frame:</p>
+ <coordinates-table
+ [coordinates]="wmInsetsSourceProviderSourceVisibleFrameOrNull()"></coordinates-table>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Position:</span>
+ &ngsp;
+ {{ wmInsetsSourceProviderPositionOrNull() }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">IsLeashReadyForDispatching:</span>
+ &ngsp;
+ {{ wmInsetsSourceProviderIsLeashReadyOrNull() }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Controllable:</span>
+ &ngsp;
+ {{ wmInsetsSourceProviderControllableOrNull() }}
+ </p>
+ </div>
+ </div>
+ <div *ngIf="wmImeControlTargetOrNull()" class="group">
+ <button
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(wmImeControlTargetOrNull())}"
+ (click)="
+ onClickShowInPropertiesPanel(wmImeControlTargetOrNull(), 'Ime Control Target')
+ ">
+ IME Control Target
+ </button>
+ <div class="left-column">
+ <p *ngIf="wmImeControlTargetTitleOrNull()" class="mat-body-1">
+ <span class="mat-body-2">Title:</span>
+ &ngsp;
+ {{ wmImeControlTargetTitleOrNull() }}
+ </p>
+ </div>
+ </div>
+ <div *ngIf="wmImeInputTargetOrNull()" class="group">
+ <button
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(wmImeInputTargetOrNull())}"
+ (click)="onClickShowInPropertiesPanel(wmImeInputTargetOrNull(), 'Ime Input Target')">
+ IME Input Target
+ </button>
+ <div class="left-column">
+ <p *ngIf="wmImeInputTargetTitleOrNull()" class="mat-body-1">
+ <span class="mat-body-2">Title:</span>
+ &ngsp;
+ {{ wmImeInputTargetTitleOrNull() }}
+ </p>
+ </div>
+ </div>
+ <div *ngIf="wmImeLayeringTargetOrNull()" class="group">
+ <button
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(wmImeLayeringTargetOrNull())}"
+ (click)="
+ onClickShowInPropertiesPanel(wmImeLayeringTargetOrNull(), 'Ime Layering Target')
+ ">
+ IME Layering Target
+ </button>
+ <div class="left-column">
+ <p *ngIf="wmImeLayeringTargetTitleOrNull()" class="mat-body-1">
+ <span class="mat-body-2">Title:</span>
+ &ngsp;
+ {{ wmImeLayeringTargetTitleOrNull() }}
+ </p>
+ </div>
+ </div>
+ </ng-container>
+
+ <ng-container *ngIf="!isImeManagerService">
+ <!-- Ime Client or Ime Service -->
+ <div class="group">
+ <button
+ *ngIf="wmProtoOrNull()"
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(wmProtoOrNull())}"
+ (click)="onClickShowInPropertiesPanel(wmProtoOrNull(), additionalProperties.wm?.name)">
+ WMState
+ </button>
+ <h3 *ngIf="!wmProtoOrNull()" class="group-header mat-subheading-2">WMState</h3>
+ <div class="left-column">
+ <p *ngIf="additionalProperties.wm" class="mat-body-1">
+ {{ additionalProperties.wm.name }}
+ </p>
+ <p *ngIf="!additionalProperties.wm" class="mat-body-1">
+ There is no corresponding WMState entry.
+ </p>
+ </div>
+ </div>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">SFLayer</h3>
+ <div class="left-column">
+ <p *ngIf="additionalProperties.sf" class="mat-body-1">
+ {{ additionalProperties.sf.name }}
+ </p>
+ <p *ngIf="!additionalProperties.sf" class="mat-body-1">
+ There is no corresponding SFLayer entry.
+ </p>
+ </div>
+ </div>
+ <div *ngIf="additionalProperties.wm" class="group">
+ <h3 class="group-header mat-subheading-2">Focus</h3>
+ <div class="left-column">
+ <p class="mat-body-1">
+ <span class="mat-body-2">Focused App:</span>
+ &ngsp;
+ {{ additionalProperties.wm.focusedApp }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Focused Activity:</span>
+ &ngsp;
+ {{ additionalProperties.wm.focusedActivity }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Focused Window:</span>
+ &ngsp;
+ {{ additionalProperties.wm.focusedWindow }}
+ </p>
+ <p *ngIf="additionalProperties.sf" class="mat-body-1">
+ <span class="mat-body-2">Focused Window Color:</span>
+ &ngsp;
+ {{ additionalProperties.sf.focusedWindow?.color }}
+ </p>
+ <p class="mat-body-2">Input Control Target Frame:</p>
+ <coordinates-table [coordinates]="wmControlTargetFrameOrNull()"></coordinates-table>
+ </div>
+ </div>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Visibility</h3>
+ <div class="left-column">
+ <p *ngIf="additionalProperties.wm" class="mat-body-1">
+ <span class="mat-body-2">InputMethod Window:</span>
+ &ngsp;
+ {{ additionalProperties.wm.isInputMethodWindowVisible }}
+ </p>
+ <p *ngIf="additionalProperties.sf" class="mat-body-1">
+ <span class="mat-body-2">InputMethod Surface:</span>
+ &ngsp;
+ {{ additionalProperties.sf.inputMethodSurface?.isInputMethodSurfaceVisible ?? false }}
+ </p>
+ </div>
+ </div>
+ <div *ngIf="additionalProperties.sf" class="group">
+ <button
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(additionalProperties.sf.imeContainer)}"
+ (click)="onClickShowInPropertiesPanel(additionalProperties.sf.imeContainer)">
+ Ime Container
+ </button>
+ <div class="left-column">
+ <p class="mat-body-1">
+ <span class="mat-body-2">ZOrderRelativeOfId:</span>
+ &ngsp;
+ {{ additionalProperties.sf.imeContainer.zOrderRelativeOfId }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Z:</span>
+ &ngsp;
+ {{ additionalProperties.sf.imeContainer.z }}
+ </p>
+ </div>
+ </div>
+ <div *ngIf="additionalProperties.sf" class="group">
+ <button
+ color="primary"
+ mat-button
+ class="group-header"
+ [class]="{selected: isHighlighted(additionalProperties.sf.inputMethodSurface)}"
+ (click)="onClickShowInPropertiesPanel(additionalProperties.sf.inputMethodSurface)">
+ Input Method Surface
+ </button>
+ <div class="left-column">
+ <p class="mat-body-2">ScreenBounds:</p>
+ <coordinates-table
+ [coordinates]="sfImeContainerScreenBoundsOrNull()"></coordinates-table>
+ </div>
+ <div class="right-column">
+ <p class="mat-body-2">Rect:</p>
+ <coordinates-table [coordinates]="sfImeContainerRectOrNull()"></coordinates-table>
+ </div>
+ </div>
+ </ng-container>
+ </div>
+ `,
+ styles: [
+ `
+ .view-header {
+ border-bottom: 1px solid var(--border-color);
+ }
+
+ .additional-properties-content {
+ height: 0;
+ flex-grow: 1;
+ overflow-y: auto;
+ }
+
+ .group {
+ padding: 8px;
+ display: flex;
+ flex-direction: row;
+ border-bottom: 1px solid var(--border-color);
+ }
+
+ .mat-body-1 {
+ overflow-wrap: anywhere;
+ }
+
+ .group-header {
+ height: 100%;
+ width: 80px;
+ padding: 0;
+ text-align: center;
+ line-height: normal;
+ white-space: normal;
+ }
+
+ p.group-header {
+ color: gray;
+ }
+
+ .left-column {
+ flex: 1;
+ padding: 0 5px;
+ }
+
+ .right-column {
+ flex: 1;
+ padding: 0 5px;
+ }
+ `,
+ ],
+})
+export class ImeAdditionalPropertiesComponent {
+ @Input() additionalProperties!: ImeAdditionalProperties;
+ @Input() isImeManagerService?: boolean;
+ @Input() highlightedItems: string[] = [];
+
+ constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
+
+ isHighlighted(item: any) {
+ return UiTreeUtils.isHighlighted(item, this.highlightedItems);
+ }
+
+ formatProto(item: any) {
+ if (item?.prettyPrint) {
+ return item.prettyPrint();
+ }
+ }
+
+ wmProtoOrNull() {
+ return this.additionalProperties.wm?.proto;
+ }
+
+ wmInsetsSourceProviderOrNull() {
+ return this.additionalProperties.wm?.protoImeInsetsSourceProvider
+ ? Object.assign(
+ {name: 'Ime Insets Source Provider'},
+ this.additionalProperties.wm.protoImeInsetsSourceProvider
+ )
+ : null;
+ }
+
+ wmControlTargetFrameOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider
+ ?.controlTarget?.windowFrames?.frame || 'null'
+ );
+ }
+
+ wmInsetsSourceProviderPositionOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider?.control
+ ?.position || 'null'
+ );
+ }
+
+ wmInsetsSourceProviderIsLeashReadyOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider
+ ?.isLeashReadyForDispatching || 'null'
+ );
+ }
+
+ wmInsetsSourceProviderControllableOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider
+ ?.controllable || 'null'
+ );
+ }
+
+ wmInsetsSourceProviderSourceFrameOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider?.source
+ ?.frame || 'null'
+ );
+ }
+
+ wmInsetsSourceProviderSourceVisibleOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider?.source
+ ?.visible || 'null'
+ );
+ }
+
+ wmInsetsSourceProviderSourceVisibleFrameOrNull() {
+ return (
+ this.additionalProperties.wm?.protoImeInsetsSourceProvider?.insetsSourceProvider?.source
+ ?.visibleFrame || 'null'
+ );
+ }
+
+ wmImeControlTargetOrNull() {
+ return this.additionalProperties?.wm?.protoImeControlTarget
+ ? Object.assign(
+ {name: 'IME Control Target'},
+ this.additionalProperties.wm.protoImeControlTarget
+ )
+ : null;
+ }
+
+ wmImeControlTargetTitleOrNull() {
+ return (
+ this.additionalProperties?.wm?.protoImeControlTarget?.windowContainer?.identifier?.title ||
+ 'null'
+ );
+ }
+
+ wmImeInputTargetOrNull() {
+ return this.additionalProperties?.wm?.protoImeInputTarget
+ ? Object.assign({name: 'IME Input Target'}, this.additionalProperties.wm.protoImeInputTarget)
+ : null;
+ }
+
+ wmImeInputTargetTitleOrNull() {
+ return (
+ this.additionalProperties?.wm?.protoImeInputTarget?.windowContainer?.identifier?.title ||
+ 'null'
+ );
+ }
+ wmImeLayeringTargetOrNull() {
+ return this.additionalProperties?.wm?.protoImeLayeringTarget
+ ? Object.assign(
+ {name: 'IME Layering Target'},
+ this.additionalProperties.wm.protoImeLayeringTarget
+ )
+ : null;
+ }
+
+ wmImeLayeringTargetTitleOrNull() {
+ return (
+ this.additionalProperties?.wm?.protoImeLayeringTarget?.windowContainer?.identifier?.title ||
+ 'null'
+ );
+ }
+
+ sfImeContainerScreenBoundsOrNull() {
+ return this.additionalProperties.sf?.inputMethodSurface?.screenBounds || 'null';
+ }
+
+ sfImeContainerRectOrNull() {
+ return this.additionalProperties.sf?.inputMethodSurface?.rect || 'null';
+ }
+
+ isAllPropertiesNull() {
+ if (this.isImeManagerService) {
+ return !this.additionalProperties.wm;
+ } else {
+ return !(this.additionalProperties.wm || this.additionalProperties.sf);
+ }
+ }
+
+ onClickShowInPropertiesPanel(item: any, name?: string) {
+ if (item.id) {
+ this.updateHighlightedItems(item.id);
+ } else {
+ this.updateAdditionalPropertySelected(item, name);
+ }
+ }
+
+ private updateHighlightedItems(newId: string) {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.HighlightedChange, {
+ bubbles: true,
+ detail: {id: newId},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ private updateAdditionalPropertySelected(item: any, name?: string) {
+ const itemWrapper = {
+ name,
+ proto: item,
+ };
+ const event: CustomEvent = new CustomEvent(ViewerEvents.AdditionalPropertySelected, {
+ bubbles: true,
+ detail: {selectedItem: itemWrapper},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+}
diff --git a/tools/winscope/src/viewers/components/ime_additional_properties_component_test.ts b/tools/winscope/src/viewers/components/ime_additional_properties_component_test.ts
new file mode 100644
index 0000000..501c22c
--- /dev/null
+++ b/tools/winscope/src/viewers/components/ime_additional_properties_component_test.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {ImeAdditionalPropertiesComponent} from './ime_additional_properties_component';
+
+describe('ImeAdditionalPropertiesComponent', () => {
+ let fixture: ComponentFixture<ImeAdditionalPropertiesComponent>;
+ let component: ImeAdditionalPropertiesComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MatDividerModule],
+ declarations: [ImeAdditionalPropertiesComponent],
+ schemas: [],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ImeAdditionalPropertiesComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/properties_component.ts b/tools/winscope/src/viewers/components/properties_component.ts
new file mode 100644
index 0000000..9162c04
--- /dev/null
+++ b/tools/winscope/src/viewers/components/properties_component.ts
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 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 {Component, ElementRef, Inject, Input} from '@angular/core';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {PropertiesTreeNode, Terminal} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+
+@Component({
+ selector: 'properties-view',
+ template: `
+ <div class="view-header" [class.view-header-with-property-groups]="displayPropertyGroups">
+ <div class="title-filter">
+ <h2 class="properties-title mat-title">Properties</h2>
+
+ <mat-form-field>
+ <mat-label>Filter...</mat-label>
+
+ <input matInput [(ngModel)]="filterString" (ngModelChange)="filterTree()" name="filter" />
+ </mat-form-field>
+ </div>
+
+ <div class="view-controls">
+ <mat-checkbox
+ *ngFor="let option of objectKeys(userOptions)"
+ color="primary"
+ [(ngModel)]="userOptions[option].enabled"
+ (ngModelChange)="updateTree()"
+ [matTooltip]="userOptions[option].tooltip ?? ''"
+ >{{ userOptions[option].name }}</mat-checkbox
+ >
+ </div>
+
+ <property-groups
+ *ngIf="itemIsSelected() && displayPropertyGroups"
+ class="property-groups"
+ [item]="selectedFlickerItem"></property-groups>
+ </div>
+
+ <mat-divider></mat-divider>
+
+ <div class="properties-content">
+ <h3
+ *ngIf="objectKeys(propertiesTree).length > 0 && isProtoDump"
+ class="properties-title mat-subheading-2">
+ Properties - Proto Dump
+ </h3>
+
+ <div class="tree-wrapper">
+ <tree-view
+ *ngIf="objectKeys(propertiesTree).length > 0"
+ [item]="propertiesTree"
+ [showNode]="showNode"
+ [isLeaf]="isLeaf"
+ [isAlwaysCollapsed]="true"></tree-view>
+ </div>
+ </div>
+ `,
+ styles: [
+ `
+ .view-header {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+ margin-bottom: 12px;
+ }
+
+ .view-header-with-property-groups {
+ flex: 3;
+ }
+
+ .title-filter {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ }
+
+ .view-controls {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ column-gap: 10px;
+ margin-bottom: 16px;
+ }
+
+ .properties-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+ }
+
+ .property-groups {
+ height: 100%;
+ overflow-y: auto;
+ }
+
+ .tree-wrapper {
+ overflow: auto;
+ }
+ `,
+ ],
+})
+export class PropertiesComponent {
+ objectKeys = Object.keys;
+ filterString = '';
+
+ @Input() userOptions: UserOptions = {};
+ @Input() propertiesTree: PropertiesTreeNode = {};
+ @Input() selectedFlickerItem: TraceTreeNode | null = null;
+ @Input() displayPropertyGroups = false;
+ @Input() isProtoDump = false;
+
+ constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
+
+ filterTree() {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.PropertiesFilterChange, {
+ bubbles: true,
+ detail: {filterString: this.filterString},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ updateTree() {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.PropertiesUserOptionsChange, {
+ bubbles: true,
+ detail: {userOptions: this.userOptions},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+
+ showNode(item: any) {
+ return (
+ !(item instanceof Terminal) &&
+ !(item.name instanceof Terminal) &&
+ !(item.propertyKey instanceof Terminal)
+ );
+ }
+
+ isLeaf(item: any) {
+ return (
+ !item.children ||
+ item.children.length === 0 ||
+ item.children.filter((c: any) => !(c instanceof Terminal)).length === 0
+ );
+ }
+
+ itemIsSelected() {
+ return this.selectedFlickerItem && Object.keys(this.selectedFlickerItem).length > 0;
+ }
+}
diff --git a/tools/winscope/src/viewers/components/properties_component_test.ts b/tools/winscope/src/viewers/components/properties_component_test.ts
new file mode 100644
index 0000000..9098ff3
--- /dev/null
+++ b/tools/winscope/src/viewers/components/properties_component_test.ts
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatInputModule} from '@angular/material/input';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {PropertiesComponent} from './properties_component';
+import {PropertyGroupsComponent} from './property_groups_component';
+import {TreeComponent} from './tree_component';
+
+describe('PropertiesComponent', () => {
+ let fixture: ComponentFixture<PropertiesComponent>;
+ let component: PropertiesComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [PropertiesComponent, PropertyGroupsComponent, TreeComponent],
+ imports: [
+ CommonModule,
+ MatInputModule,
+ MatFormFieldModule,
+ MatCheckboxModule,
+ MatDividerModule,
+ BrowserAnimationsModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PropertiesComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ component.propertiesTree = {};
+ component.selectedFlickerItem = null;
+ component.userOptions = {
+ showDefaults: {
+ name: 'Show defaults',
+ enabled: false,
+ },
+ };
+ });
+
+ it('can be created', () => {
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ it('creates title', () => {
+ fixture.detectChanges();
+ const title = htmlElement.querySelector('.properties-title');
+ expect(title).toBeTruthy();
+ });
+
+ it('creates view controls', () => {
+ fixture.detectChanges();
+ const viewControls = htmlElement.querySelector('.view-controls');
+ expect(viewControls).toBeTruthy();
+ });
+
+ it('creates initial tree elements', () => {
+ fixture.detectChanges();
+ const tree = htmlElement.querySelector('.tree-wrapper');
+ expect(tree).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/properties_table_component.ts b/tools/winscope/src/viewers/components/properties_table_component.ts
new file mode 100644
index 0000000..9235abf
--- /dev/null
+++ b/tools/winscope/src/viewers/components/properties_table_component.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {TableProperties} from 'viewers/common/table_properties';
+
+@Component({
+ selector: 'properties-table',
+ template: `
+ <table class="table">
+ <tr *ngFor="let entry of objectEntries(properties)">
+ <td class="table-cell-name">
+ <p class="mat-body-1">{{ entry[0] }}</p>
+ </td>
+ <td class="table-cell-value">
+ <p class="mat-body-1">{{ entry[1] != undefined ? entry[1] : 'undefined' }}</p>
+ </td>
+ </tr>
+ </table>
+ `,
+ styles: [
+ `
+ .table {
+ width: 100%;
+ border-collapse: collapse;
+ }
+
+ .table-cell-name,
+ .table-cell-value {
+ padding: 1px 5px;
+ border: 1px solid var(--border-color);
+ overflow-wrap: anywhere;
+ }
+
+ .table-cell-name {
+ width: 20%;
+ background-color: rgba(158, 192, 200, 0.281);
+ }
+ `,
+ ],
+})
+export class PropertiesTableComponent {
+ objectEntries = Object.entries;
+
+ @Input() properties!: TableProperties;
+}
diff --git a/tools/winscope/src/viewers/components/properties_table_component_test.ts b/tools/winscope/src/viewers/components/properties_table_component_test.ts
new file mode 100644
index 0000000..9dcb8ea
--- /dev/null
+++ b/tools/winscope/src/viewers/components/properties_table_component_test.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {PropertiesTableComponent} from './properties_table_component';
+
+describe('PropertiesTableComponent', () => {
+ let fixture: ComponentFixture<PropertiesTableComponent>;
+ let component: PropertiesTableComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [PropertiesTableComponent],
+ schemas: [],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PropertiesTableComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/property_groups_component.ts b/tools/winscope/src/viewers/components/property_groups_component.ts
new file mode 100644
index 0000000..5193de1
--- /dev/null
+++ b/tools/winscope/src/viewers/components/property_groups_component.ts
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {Layer} from 'trace/flickerlib/common';
+
+@Component({
+ selector: 'property-groups',
+ template: `
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Visibility</h3>
+ <div class="left-column">
+ <p class="mat-body-1 flags">
+ <span class="mat-body-2">Flags:</span>
+ &ngsp;
+ {{ item.verboseFlags ? item.verboseFlags : item.flags }}
+ </p>
+ <p *ngFor="let reason of summary()" class="mat-body-1">
+ <span class="mat-body-2">{{ reason.key }}:</span>
+ &ngsp;
+ {{ reason.value }}
+ </p>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Geometry</h3>
+ <div class="left-column">
+ <p class="column-header mat-small">Calculated</p>
+ <p class="property mat-body-2">Transform:</p>
+ <transform-matrix
+ [transform]="item.transform"
+ [formatFloat]="formatFloat"></transform-matrix>
+ <p class="mat-body-1">
+ <span
+ class="mat-body-2"
+ matTooltip="Raw value read from proto.bounds. This is the buffer size or
+ requested crop cropped by parent bounds."
+ >Crop:</span
+ >
+ &ngsp;
+ {{ item.bounds }}
+ </p>
+
+ <p class="mat-body-1">
+ <span
+ class="mat-body-2"
+ matTooltip="Raw value read from proto.screenBounds. This is the calculated crop
+ transformed."
+ >Final Bounds:</span
+ >
+ &ngsp;
+ {{ item.screenBounds }}
+ </p>
+ </div>
+ <div class="right-column">
+ <p class="column-header mat-small">Requested</p>
+ <p class="property mat-body-2">Transform:</p>
+ <transform-matrix
+ [transform]="item.requestedTransform"
+ [formatFloat]="formatFloat"></transform-matrix>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Crop:</span>
+ &ngsp;
+ {{ item.crop ? item.crop : '[empty]' }}
+ </p>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Buffer</h3>
+ <div class="left-column">
+ <p class="mat-body-1">
+ <span class="mat-body-2">Size:</span>
+ &ngsp;
+ {{ item.activeBuffer }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Frame Number:</span>
+ &ngsp;
+ {{ item.currFrame }}
+ </p>
+ <p class="mat-body-1">
+ <span
+ class="mat-body-2"
+ matTooltip="Rotates or flips the buffer in place. Used with display transform
+ hint to cancel out any buffer transformation when sending to
+ HWC."
+ >Transform:</span
+ >
+ &ngsp;
+ {{ item.bufferTransform }}
+ </p>
+ </div>
+ <div class="right-column">
+ <p class="mat-body-1">
+ <span
+ class="mat-body-2"
+ matTooltip="Scales buffer to the frame by overriding the requested transform
+ for this item."
+ >Destination Frame:</span
+ >
+ &ngsp;
+ {{ getDestinationFrame() }}
+ </p>
+ <p *ngIf="hasIgnoreDestinationFrame()" class="mat-body-1">
+ Destination Frame ignored because item has eIgnoreDestinationFrame flag set.
+ </p>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Hierarchy</h3>
+ <div class="left-column">
+ <p class="mat-body-1">
+ <span class="mat-body-2">z-order:</span>
+ &ngsp;
+ {{ item.z }}
+ </p>
+ <p class="mat-body-1">
+ <span
+ class="mat-body-2"
+ matTooltip="item is z-ordered relative to its relative parents but its bounds
+ and other properties are inherited from its parents."
+ >relative parent:</span
+ >
+ &ngsp;
+ {{ item.zOrderRelativeOfId == -1 ? 'none' : item.zOrderRelativeOfId }}
+ </p>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Effects</h3>
+ <div class="left-column">
+ <p class="column-header mat-small">Calculated</p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Color:</span>
+ &ngsp;
+ {{ item.color }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Shadow:</span>
+ &ngsp;
+ {{ item.shadowRadius }} px
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Corner Radius:</span>
+ &ngsp;
+ {{ formatFloat(item.cornerRadius) }} px
+ </p>
+ <p class="mat-body-1">
+ <span
+ class="mat-body-2"
+ matTooltip="Crop used to define the bounds of the corner radii. If the bounds
+ are greater than the item bounds then the rounded corner will not
+ be visible."
+ >Corner Radius Crop:</span
+ >
+ &ngsp;
+ {{ item.cornerRadiusCrop }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Blur:</span>
+ &ngsp;
+ {{ item.proto?.backgroundBlurRadius ? item.proto?.backgroundBlurRadius : 0 }} px
+ </p>
+ </div>
+ <div class="right-column">
+ <p class="column-header mat-small">Requested</p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Color:</span>
+ &ngsp;
+ {{ item.requestedColor }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Shadow:</span>
+ &ngsp;
+ {{ item.proto?.requestedShadowRadius ? item.proto?.requestedShadowRadius : 0 }} px
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Corner Radius:</span>
+ &ngsp;
+ {{
+ item.proto?.requestedCornerRadius ? formatFloat(item.proto?.requestedCornerRadius) : 0
+ }}
+ px
+ </p>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="group">
+ <h3 class="group-header mat-subheading-2">Input</h3>
+ <ng-container *ngIf="hasInputChannel()">
+ <div class="left-column">
+ <p class="property mat-body-2">To Display Transform:</p>
+ <transform-matrix
+ [transform]="item.inputTransform"
+ [formatFloat]="formatFloat"></transform-matrix>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Touchable Region:</span>
+ &ngsp;
+ {{ item.inputRegion }}
+ </p>
+ </div>
+ <div class="right-column">
+ <p class="column-header mat-small">Config</p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Focusable:</span>
+ &ngsp;
+ {{ item.proto?.inputWindowInfo.focusable }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Crop touch region with item:</span>
+ &ngsp;
+ {{
+ item.proto?.inputWindowInfo.cropLayerId <= 0
+ ? "none"
+ : item.proto?.inputWindowInfo.cropLayerId
+ }}
+ </p>
+ <p class="mat-body-1">
+ <span class="mat-body-2">Replace touch region with crop:</span>
+ &ngsp;
+ {{ item.proto?.inputWindowInfo.replaceTouchableRegionWithCrop }}
+ </p>
+ </div>
+ </ng-container>
+ <div *ngIf="!hasInputChannel()" class="left-column">
+ <p class="mat-body-1">
+ <span class="mat-body-2">Input channel:</span>
+ &ngsp; not set
+ </p>
+ </div>
+ </div>
+ `,
+ styles: [
+ `
+ .group {
+ display: flex;
+ flex-direction: row;
+ padding: 8px;
+ }
+
+ .group-header {
+ width: 80px;
+ color: gray;
+ }
+
+ .left-column {
+ flex: 1;
+ padding: 0 5px;
+ }
+
+ .right-column {
+ flex: 1;
+ border: 1px solid var(--border-color);
+ border-left-width: 5px;
+ padding: 0 5px;
+ }
+
+ .column-header {
+ color: gray;
+ }
+ `,
+ ],
+})
+export class PropertyGroupsComponent {
+ @Input() item!: Layer;
+
+ hasInputChannel() {
+ return this.item.proto?.inputWindowInfo;
+ }
+
+ getDestinationFrame() {
+ const frame = this.item.proto?.destinationFrame;
+ if (frame) {
+ return ` left: ${frame.left}, top: ${frame.top}, right: ${frame.right}, bottom: ${frame.bottom}`;
+ } else return '';
+ }
+
+ hasIgnoreDestinationFrame() {
+ return (this.item.flags & 0x400) === 0x400;
+ }
+
+ formatFloat(num: number) {
+ return Math.round(num * 100) / 100;
+ }
+
+ summary(): TreeSummary {
+ const summary = [];
+
+ if (this.item?.visibilityReason?.length > 0) {
+ let reason = '';
+ if (Array.isArray(this.item.visibilityReason)) {
+ reason = this.item.visibilityReason.join(', ');
+ } else {
+ reason = this.item.visibilityReason;
+ }
+
+ summary.push({key: 'Invisible due to', value: reason});
+ }
+
+ if (this.item?.occludedBy?.length > 0) {
+ summary.push({
+ key: 'Occluded by',
+ value: this.item.occludedBy.map((it: any) => it.id).join(', '),
+ });
+ }
+
+ if (this.item?.partiallyOccludedBy?.length > 0) {
+ summary.push({
+ key: 'Partially occluded by',
+ value: this.item.partiallyOccludedBy.map((it: any) => it.id).join(', '),
+ });
+ }
+
+ if (this.item?.coveredBy?.length > 0) {
+ summary.push({
+ key: 'Covered by',
+ value: this.item.coveredBy.map((it: any) => it.id).join(', '),
+ });
+ }
+
+ return summary;
+ }
+}
+
+type TreeSummary = Array<{key: string; value: string}>;
diff --git a/tools/winscope/src/viewers/components/property_groups_component_test.ts b/tools/winscope/src/viewers/components/property_groups_component_test.ts
new file mode 100644
index 0000000..1186061
--- /dev/null
+++ b/tools/winscope/src/viewers/components/property_groups_component_test.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatTooltipModule} from '@angular/material/tooltip';
+import {LayerBuilder} from 'test/unit/layer_builder';
+import {PropertyGroupsComponent} from './property_groups_component';
+import {TransformMatrixComponent} from './transform_matrix_component';
+
+describe('PropertyGroupsComponent', () => {
+ let fixture: ComponentFixture<PropertyGroupsComponent>;
+ let component: PropertyGroupsComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MatDividerModule, MatTooltipModule],
+ declarations: [PropertyGroupsComponent, TransformMatrixComponent],
+ schemas: [],
+ }).compileComponents();
+ fixture = TestBed.createComponent(PropertyGroupsComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('it renders verbose flags if available', async () => {
+ const layer = new LayerBuilder().setFlags(3).build();
+ component.item = layer;
+ fixture.detectChanges();
+
+ const flags = htmlElement.querySelector('.flags');
+ expect(flags).toBeTruthy();
+ expect(flags!.innerHTML).toMatch('Flags:.*HIDDEN|OPAQUE \\(0x3\\)');
+ });
+
+ it('it renders numeric flags if verbose flags not available', async () => {
+ const layer = new LayerBuilder().setFlags(0).build();
+ component.item = layer;
+ fixture.detectChanges();
+
+ const flags = htmlElement.querySelector('.flags');
+ expect(flags).toBeTruthy();
+ expect(flags!.innerHTML).toMatch('Flags:.*0');
+ });
+});
diff --git a/tools/winscope/src/viewers/components/rects/canvas.ts b/tools/winscope/src/viewers/components/rects/canvas.ts
new file mode 100644
index 0000000..078c9ee
--- /dev/null
+++ b/tools/winscope/src/viewers/components/rects/canvas.ts
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 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 * as THREE from 'three';
+import {CSS2DObject, CSS2DRenderer} from 'three/examples/jsm/renderers/CSS2DRenderer';
+import {Rectangle} from 'viewers/common/rectangle';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+import {Circle3D, ColorType, Label3D, Point3D, Rect3D, Scene3D, Transform3D} from './types3d';
+
+export class Canvas {
+ private static readonly TARGET_SCENE_DIAGONAL = 4;
+ private static readonly RECT_COLOR_HIGHLIGHTED = new THREE.Color(0xd2e3fc);
+ private static readonly RECT_EDGE_COLOR = 0x000000;
+ private static readonly RECT_EDGE_COLOR_ROUNDED = 0x848884;
+ private static readonly LABEL_CIRCLE_COLOR = 0x000000;
+ private static readonly LABEL_LINE_COLOR = 0x000000;
+ private static readonly LABEL_LINE_COLOR_HIGHLIGHTED = 0x808080;
+ private static readonly OPACITY_REGULAR = 0.75;
+ private static readonly OPACITY_OVERSIZED = 0.25;
+
+ private canvasRects: HTMLCanvasElement;
+ private canvasLabels: HTMLElement;
+ private camera?: THREE.OrthographicCamera;
+ private scene?: THREE.Scene;
+ private renderer?: THREE.WebGLRenderer;
+ private labelRenderer?: CSS2DRenderer;
+ private rects: Rectangle[] = [];
+ private clickableObjects: THREE.Object3D[] = [];
+
+ constructor(canvasRects: HTMLCanvasElement, canvasLabels: HTMLElement) {
+ this.canvasRects = canvasRects;
+ this.canvasLabels = canvasLabels;
+ }
+
+ draw(scene: Scene3D) {
+ // Must set 100% width and height so the HTML element expands to the parent's
+ // boundaries and the correct clientWidth and clientHeight values can be read
+ this.canvasRects.style.width = '100%';
+ this.canvasRects.style.height = '100%';
+ let widthAspectRatioAdjustFactor: number;
+ let heightAspectRatioAdjustFactor: number;
+
+ if (this.canvasRects.clientWidth > this.canvasRects.clientHeight) {
+ heightAspectRatioAdjustFactor = 1;
+ widthAspectRatioAdjustFactor = this.canvasRects.clientWidth / this.canvasRects.clientHeight;
+ } else {
+ heightAspectRatioAdjustFactor = this.canvasRects.clientHeight / this.canvasRects.clientWidth;
+ widthAspectRatioAdjustFactor = 1;
+ }
+
+ const cameraWidth = Canvas.TARGET_SCENE_DIAGONAL * widthAspectRatioAdjustFactor;
+ const cameraHeight = Canvas.TARGET_SCENE_DIAGONAL * heightAspectRatioAdjustFactor;
+
+ const panFactorX = scene.camera.panScreenDistance.dx / this.canvasRects.clientWidth;
+ const panFactorY = scene.camera.panScreenDistance.dy / this.canvasRects.clientHeight;
+
+ this.scene = new THREE.Scene();
+ const scaleFactor =
+ (Canvas.TARGET_SCENE_DIAGONAL / scene.boundingBox.diagonal) * scene.camera.zoomFactor;
+ this.scene.scale.set(scaleFactor, -scaleFactor, scaleFactor);
+ this.scene.translateX(scaleFactor * -scene.boundingBox.center.x + cameraWidth * panFactorX);
+ this.scene.translateY(scaleFactor * scene.boundingBox.center.y - cameraHeight * panFactorY);
+ this.scene.translateZ(scaleFactor * -scene.boundingBox.center.z);
+
+ this.camera = new THREE.OrthographicCamera(
+ -cameraWidth / 2,
+ cameraWidth / 2,
+ cameraHeight / 2,
+ -cameraHeight / 2,
+ 0,
+ 100
+ );
+
+ const rotationAngleX = (scene.camera.rotationFactor * Math.PI * 45) / 360;
+ const rotationAngleY = rotationAngleX * 1.5;
+ const cameraPosition = new THREE.Vector3(0, 0, Canvas.TARGET_SCENE_DIAGONAL);
+ cameraPosition.applyAxisAngle(new THREE.Vector3(1, 0, 0), -rotationAngleX);
+ cameraPosition.applyAxisAngle(new THREE.Vector3(0, 1, 0), rotationAngleY);
+
+ this.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
+ this.camera.lookAt(0, 0, 0);
+
+ this.renderer = new THREE.WebGLRenderer({
+ antialias: true,
+ canvas: this.canvasRects,
+ alpha: true,
+ });
+
+ this.labelRenderer = new CSS2DRenderer({element: this.canvasLabels});
+
+ // set various factors for shading and shifting
+ const numberOfRects = this.rects.length;
+ this.drawRects(scene.rects);
+ this.drawLabels(scene.labels);
+
+ this.renderer.setSize(this.canvasRects!.clientWidth, this.canvasRects!.clientHeight);
+ this.renderer.setPixelRatio(window.devicePixelRatio);
+ this.renderer.compile(this.scene, this.camera);
+ this.renderer.render(this.scene, this.camera);
+
+ this.labelRenderer.setSize(this.canvasRects!.clientWidth, this.canvasRects!.clientHeight);
+ this.labelRenderer.render(this.scene, this.camera);
+ }
+
+ getClickedRectId(x: number, y: number, z: number): undefined | string {
+ const clickPosition = new THREE.Vector3(x, y, z);
+ const raycaster = new THREE.Raycaster();
+ raycaster.setFromCamera(clickPosition, this.camera!);
+ const intersected = raycaster.intersectObjects(this.clickableObjects);
+ if (intersected.length > 0) {
+ return intersected[0].object.name;
+ }
+ return undefined;
+ }
+
+ private drawRects(rects: Rect3D[]) {
+ this.clickableObjects = [];
+ rects.forEach((rect) => {
+ const rectMesh = this.makeRectMesh(rect);
+ const transform = this.toMatrix4(rect.transform);
+ rectMesh.applyMatrix4(transform);
+
+ this.scene?.add(rectMesh);
+
+ if (rect.isClickable) {
+ this.clickableObjects.push(rectMesh);
+ }
+ });
+ }
+
+ private drawLabels(labels: Label3D[]) {
+ this.clearLabels();
+ labels.forEach((label) => {
+ const circleMesh = this.makeLabelCircleMesh(label.circle);
+ this.scene?.add(circleMesh);
+
+ const linePoints = label.linePoints.map((point: Point3D) => {
+ return new THREE.Vector3(point.x, point.y, point.z);
+ });
+ const lineGeometry = new THREE.BufferGeometry().setFromPoints(linePoints);
+ const lineMaterial = new THREE.LineBasicMaterial({
+ color: label.isHighlighted ? Canvas.LABEL_LINE_COLOR_HIGHLIGHTED : Canvas.LABEL_LINE_COLOR,
+ });
+ const line = new THREE.Line(lineGeometry, lineMaterial);
+ this.scene?.add(line);
+
+ this.drawLabelTextHtml(label);
+ });
+ }
+
+ private drawLabelTextHtml(label: Label3D) {
+ // Add rectangle label
+ const spanText: HTMLElement = document.createElement('span');
+ spanText.innerText = label.text;
+ spanText.className = 'mat-body-1';
+
+ // Hack: transparent/placeholder text used to push the visible text towards left
+ // (towards negative x) and properly align it with the label's vertical segment
+ const spanPlaceholder: HTMLElement = document.createElement('span');
+ spanPlaceholder.innerText = label.text;
+ spanPlaceholder.className = 'mat-body-1';
+ spanPlaceholder.style.opacity = '0';
+
+ const div: HTMLElement = document.createElement('div');
+ div.className = 'rect-label';
+ div.style.display = 'inline';
+ div.appendChild(spanText);
+ div.appendChild(spanPlaceholder);
+
+ div.style.marginTop = '5px';
+ if (label.isHighlighted) {
+ div.style.color = 'gray';
+ }
+ div.style.pointerEvents = 'auto';
+ div.style.cursor = 'pointer';
+ div.addEventListener('click', (event) =>
+ this.propagateUpdateHighlightedItems(event, label.rectId)
+ );
+
+ const labelCss = new CSS2DObject(div);
+ labelCss.position.set(label.textCenter.x, label.textCenter.y, label.textCenter.z);
+
+ this.scene?.add(labelCss);
+ }
+
+ private toMatrix4(transform: Transform3D): THREE.Matrix4 {
+ return new THREE.Matrix4().set(
+ transform.dsdx,
+ transform.dsdy,
+ 0,
+ transform.tx,
+ transform.dtdx,
+ transform.dtdy,
+ 0,
+ transform.ty,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1
+ );
+ }
+
+ private makeRectMesh(rect: Rect3D): THREE.Mesh {
+ const rectShape = this.createRectShape(rect);
+ const rectGeometry = new THREE.ShapeGeometry(rectShape);
+ const rectBorders = this.createRectBorders(rect, rectGeometry);
+
+ let opacity = Canvas.OPACITY_REGULAR;
+ if (rect.isOversized) {
+ opacity = Canvas.OPACITY_OVERSIZED;
+ }
+
+ // Crate mesh to draw
+ const mesh = new THREE.Mesh(
+ rectGeometry,
+ new THREE.MeshBasicMaterial({
+ color: this.getColor(rect),
+ opacity,
+ transparent: true,
+ })
+ );
+
+ mesh.add(rectBorders);
+ mesh.position.x = 0;
+ mesh.position.y = 0;
+ mesh.position.z = rect.topLeft.z;
+ mesh.name = rect.id;
+
+ return mesh;
+ }
+
+ private createRectShape(rect: Rect3D): THREE.Shape {
+ const bottomLeft: Point3D = {x: rect.topLeft.x, y: rect.bottomRight.y, z: rect.topLeft.z};
+ const topRight: Point3D = {x: rect.bottomRight.x, y: rect.topLeft.y, z: rect.bottomRight.z};
+
+ // Limit corner radius if larger than height/2 (or width/2)
+ const height = rect.bottomRight.y - rect.topLeft.y;
+ const width = rect.bottomRight.x - rect.topLeft.x;
+ const minEdge = Math.min(height, width);
+ let cornerRadius = Math.min(rect.cornerRadius, minEdge / 2);
+
+ // Force radius > 0, because radius === 0 could result in weird triangular shapes
+ // being drawn instead of rectangles. Seems like quadraticCurveTo() doesn't
+ // always handle properly the case with radius === 0.
+ cornerRadius = Math.max(cornerRadius, 0.01);
+
+ // Create (rounded) rect shape
+ return new THREE.Shape()
+ .moveTo(rect.topLeft.x, rect.topLeft.y + cornerRadius)
+ .lineTo(bottomLeft.x, bottomLeft.y - cornerRadius)
+ .quadraticCurveTo(bottomLeft.x, bottomLeft.y, bottomLeft.x + cornerRadius, bottomLeft.y)
+ .lineTo(rect.bottomRight.x - cornerRadius, rect.bottomRight.y)
+ .quadraticCurveTo(
+ rect.bottomRight.x,
+ rect.bottomRight.y,
+ rect.bottomRight.x,
+ rect.bottomRight.y - cornerRadius
+ )
+ .lineTo(topRight.x, topRight.y + cornerRadius)
+ .quadraticCurveTo(topRight.x, topRight.y, topRight.x - cornerRadius, topRight.y)
+ .lineTo(rect.topLeft.x + cornerRadius, rect.topLeft.y)
+ .quadraticCurveTo(
+ rect.topLeft.x,
+ rect.topLeft.y,
+ rect.topLeft.x,
+ rect.topLeft.y + cornerRadius
+ );
+ }
+
+ private getColor(rect: Rect3D): THREE.Color {
+ switch (rect.colorType) {
+ case ColorType.VISIBLE: {
+ // green (darkness depends on z order)
+ const red = ((200 - 45) * rect.darkFactor + 45) / 255;
+ const green = ((232 - 182) * rect.darkFactor + 182) / 255;
+ const blue = ((183 - 44) * rect.darkFactor + 44) / 255;
+ return new THREE.Color(red, green, blue);
+ }
+ case ColorType.NOT_VISIBLE: {
+ // gray (darkness depends on z order)
+ const lower = 120;
+ const upper = 220;
+ const darkness = ((upper - lower) * rect.darkFactor + lower) / 255;
+ return new THREE.Color(darkness, darkness, darkness);
+ }
+ case ColorType.HIGHLIGHTED: {
+ return Canvas.RECT_COLOR_HIGHLIGHTED;
+ }
+ default: {
+ throw new Error(`Unexpected color type: ${rect.colorType}`);
+ }
+ }
+ }
+
+ private createRectBorders(rect: Rect3D, rectGeometry: THREE.ShapeGeometry): THREE.LineSegments {
+ // create line edges for rect
+ const edgeGeo = new THREE.EdgesGeometry(rectGeometry);
+ let edgeMaterial: THREE.Material;
+ if (rect.cornerRadius) {
+ edgeMaterial = new THREE.LineBasicMaterial({
+ color: Canvas.RECT_EDGE_COLOR_ROUNDED,
+ linewidth: 1,
+ });
+ } else {
+ edgeMaterial = new THREE.LineBasicMaterial({
+ color: Canvas.RECT_EDGE_COLOR,
+ linewidth: 1,
+ });
+ }
+ const lineSegments = new THREE.LineSegments(edgeGeo, edgeMaterial);
+ lineSegments.computeLineDistances();
+ return lineSegments;
+ }
+
+ private makeLabelCircleMesh(circle: Circle3D): THREE.Mesh {
+ const geometry = new THREE.CircleGeometry(circle.radius, 20);
+ const material = new THREE.MeshBasicMaterial({color: Canvas.LABEL_CIRCLE_COLOR});
+ const mesh = new THREE.Mesh(geometry, material);
+ mesh.position.set(circle.center.x, circle.center.y, circle.center.z);
+ return mesh;
+ }
+
+ private propagateUpdateHighlightedItems(event: MouseEvent, newId: string) {
+ event.preventDefault();
+ const highlightedChangeEvent: CustomEvent = new CustomEvent(ViewerEvents.HighlightedChange, {
+ bubbles: true,
+ detail: {id: newId},
+ });
+ event.target?.dispatchEvent(highlightedChangeEvent);
+ }
+
+ private clearLabels() {
+ this.canvasLabels.innerHTML = '';
+ }
+}
diff --git a/tools/winscope/src/viewers/components/rects/mapper3d.ts b/tools/winscope/src/viewers/components/rects/mapper3d.ts
new file mode 100644
index 0000000..5fec89a
--- /dev/null
+++ b/tools/winscope/src/viewers/components/rects/mapper3d.ts
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2022 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 {Rectangle, Size} from 'viewers/common/rectangle';
+import {
+ Box3D,
+ ColorType,
+ Distance2D,
+ Label3D,
+ Point3D,
+ Rect3D,
+ Scene3D,
+ Transform3D,
+} from './types3d';
+
+class Mapper3D {
+ private static readonly CAMERA_ROTATION_FACTOR_INIT = 1;
+ private static readonly Z_SPACING_FACTOR_INIT = 1;
+ private static readonly Z_SPACING_MAX = 200;
+ private static readonly LABEL_FIRST_Y_OFFSET = 100;
+ private static readonly LABEL_TEXT_Y_SPACING = 200;
+ private static readonly LABEL_CIRCLE_RADIUS = 15;
+ private static readonly ZOOM_FACTOR_INIT = 1;
+ private static readonly ZOOM_FACTOR_MIN = 0.1;
+ private static readonly ZOOM_FACTOR_MAX = 8.5;
+ private static readonly ZOOM_FACTOR_STEP = 0.2;
+ private static readonly IDENTITY_TRANSFORM: Transform3D = {
+ dsdx: 1,
+ dsdy: 0,
+ tx: 0,
+ dtdx: 0,
+ dtdy: 1,
+ ty: 0,
+ };
+
+ private rects: Rectangle[] = [];
+ private highlightedRectIds: string[] = [];
+ private cameraRotationFactor = Mapper3D.CAMERA_ROTATION_FACTOR_INIT;
+ private zSpacingFactor = Mapper3D.Z_SPACING_FACTOR_INIT;
+ private zoomFactor = Mapper3D.ZOOM_FACTOR_INIT;
+ private panScreenDistance: Distance2D = new Distance2D(0, 0);
+ private showOnlyVisibleMode = false; // by default show all
+ private showVirtualMode = false; // by default don't show virtual displays
+ private currentDisplayId = 0; // default stack id is usually 0
+
+ setRects(rects: Rectangle[]) {
+ this.rects = rects;
+ }
+
+ setHighlightedRectIds(ids: string[]) {
+ this.highlightedRectIds = ids;
+ }
+
+ getCameraRotationFactor(): number {
+ return this.cameraRotationFactor;
+ }
+
+ setCameraRotationFactor(factor: number) {
+ this.cameraRotationFactor = Math.min(Math.max(factor, 0), 1);
+ }
+
+ getZSpacingFactor(): number {
+ return this.zSpacingFactor;
+ }
+
+ setZSpacingFactor(factor: number) {
+ this.zSpacingFactor = Math.min(Math.max(factor, 0), 1);
+ }
+
+ increaseZoomFactor() {
+ this.zoomFactor += Mapper3D.ZOOM_FACTOR_STEP;
+ this.zoomFactor = Math.min(this.zoomFactor, Mapper3D.ZOOM_FACTOR_MAX);
+ }
+
+ decreaseZoomFactor() {
+ this.zoomFactor -= Mapper3D.ZOOM_FACTOR_STEP;
+ this.zoomFactor = Math.max(this.zoomFactor, Mapper3D.ZOOM_FACTOR_MIN);
+ }
+
+ addPanScreenDistance(distance: Distance2D) {
+ this.panScreenDistance.dx += distance.dx;
+ this.panScreenDistance.dy += distance.dy;
+ }
+
+ resetCamera() {
+ this.cameraRotationFactor = Mapper3D.CAMERA_ROTATION_FACTOR_INIT;
+ this.zSpacingFactor = Mapper3D.Z_SPACING_FACTOR_INIT;
+ this.zoomFactor = Mapper3D.ZOOM_FACTOR_INIT;
+ this.panScreenDistance.dx = 0;
+ this.panScreenDistance.dy = 0;
+ }
+
+ getShowOnlyVisibleMode(): boolean {
+ return this.showOnlyVisibleMode;
+ }
+
+ setShowOnlyVisibleMode(enabled: boolean) {
+ this.showOnlyVisibleMode = enabled;
+ }
+
+ getShowVirtualMode(): boolean {
+ return this.showVirtualMode;
+ }
+
+ setShowVirtualMode(enabled: boolean) {
+ this.showVirtualMode = enabled;
+ }
+
+ getCurrentDisplayId(): number {
+ return this.currentDisplayId;
+ }
+
+ setCurrentDisplayId(id: number) {
+ this.currentDisplayId = id;
+ }
+
+ computeScene(): Scene3D {
+ const rects2d = this.selectRectsToDraw(this.rects);
+ const rects3d = this.computeRects(rects2d);
+ const labels3d = this.computeLabels(rects2d, rects3d);
+ const boundingBox = this.computeBoundingBox(rects3d, labels3d);
+
+ const scene: Scene3D = {
+ boundingBox,
+ camera: {
+ rotationFactor: this.cameraRotationFactor,
+ zoomFactor: this.zoomFactor,
+ panScreenDistance: this.panScreenDistance,
+ },
+ rects: rects3d,
+ labels: labels3d,
+ };
+
+ return scene;
+ }
+
+ private selectRectsToDraw(rects: Rectangle[]): Rectangle[] {
+ rects = rects.filter((rect) => rect.displayId === this.currentDisplayId);
+
+ if (this.showOnlyVisibleMode) {
+ rects = rects.filter((rect) => rect.isVisible || rect.isDisplay);
+ }
+
+ if (!this.showVirtualMode) {
+ rects = rects.filter((rect) => !rect.isVirtual);
+ }
+
+ return rects;
+ }
+
+ private computeRects(rects2d: Rectangle[]): Rect3D[] {
+ let visibleRectsSoFar = 0;
+ let visibleRectsTotal = 0;
+ let nonVisibleRectsSoFar = 0;
+ let nonVisibleRectsTotal = 0;
+
+ rects2d.forEach((rect) => {
+ if (rect.isVisible) {
+ ++visibleRectsTotal;
+ } else {
+ ++nonVisibleRectsTotal;
+ }
+ });
+
+ let z = 0;
+
+ const maxDisplaySize = this.getMaxDisplaySize(rects2d);
+
+ const rects3d = rects2d.map((rect2d): Rect3D => {
+ z -= Mapper3D.Z_SPACING_MAX * this.zSpacingFactor;
+
+ const darkFactor = rect2d.isVisible
+ ? (visibleRectsTotal - visibleRectsSoFar++) / visibleRectsTotal
+ : (nonVisibleRectsTotal - nonVisibleRectsSoFar++) / nonVisibleRectsTotal;
+
+ const rect = {
+ id: rect2d.id,
+ topLeft: {
+ x: rect2d.topLeft.x,
+ y: rect2d.topLeft.y,
+ z,
+ },
+ bottomRight: {
+ x: rect2d.bottomRight.x,
+ y: rect2d.bottomRight.y,
+ z,
+ },
+ isOversized: false,
+ cornerRadius: rect2d.cornerRadius,
+ darkFactor,
+ colorType: this.getColorType(rect2d),
+ isClickable: rect2d.isClickable,
+ transform: this.getTransform(rect2d),
+ };
+ return this.cropOversizedRect(rect, maxDisplaySize);
+ });
+
+ return rects3d;
+ }
+
+ private getColorType(rect2d: Rectangle): ColorType {
+ let colorType: ColorType;
+ if (this.highlightedRectIds.includes(rect2d.id)) {
+ colorType = ColorType.HIGHLIGHTED;
+ } else if (rect2d.isVisible) {
+ colorType = ColorType.VISIBLE;
+ } else {
+ colorType = ColorType.NOT_VISIBLE;
+ }
+ return colorType;
+ }
+
+ private getTransform(rect2d: Rectangle): Transform3D {
+ let transform: Transform3D;
+ if (rect2d.transform?.matrix) {
+ transform = {
+ dsdx: rect2d.transform.matrix.dsdx,
+ dsdy: rect2d.transform.matrix.dsdy,
+ tx: rect2d.transform.matrix.tx,
+ dtdx: rect2d.transform.matrix.dtdx,
+ dtdy: rect2d.transform.matrix.dtdy,
+ ty: rect2d.transform.matrix.ty,
+ };
+ } else {
+ transform = Mapper3D.IDENTITY_TRANSFORM;
+ }
+ return transform;
+ }
+
+ private getMaxDisplaySize(rects2d: Rectangle[]): Size {
+ const displays = rects2d.filter((rect2d) => rect2d.isDisplay);
+
+ let maxWidth = 0;
+ let maxHeight = 0;
+ if (displays.length > 0) {
+ maxWidth = Math.max(
+ ...displays.map((rect2d): number => Math.abs(rect2d.topLeft.x - rect2d.bottomRight.x))
+ );
+
+ maxHeight = Math.max(
+ ...displays.map((rect2d): number => Math.abs(rect2d.topLeft.y - rect2d.bottomRight.y))
+ );
+ }
+ return {
+ width: maxWidth,
+ height: maxHeight,
+ };
+ }
+
+ private cropOversizedRect(rect3d: Rect3D, maxDisplaySize: Size): Rect3D {
+ // Arbitrary max size for a rect (2x the maximum display)
+ let maxDimension = Number.MAX_VALUE;
+ if (maxDisplaySize.height > 0) {
+ maxDimension = Math.max(maxDisplaySize.width, maxDisplaySize.height) * 2;
+ }
+
+ const height = Math.abs(rect3d.topLeft.y - rect3d.bottomRight.y);
+ const width = Math.abs(rect3d.topLeft.x - rect3d.bottomRight.x);
+
+ if (width > maxDimension) {
+ rect3d.isOversized = true;
+ (rect3d.topLeft.x = (maxDimension - maxDisplaySize.width / 2) * -1),
+ (rect3d.bottomRight.x = maxDimension);
+ }
+ if (height > maxDimension) {
+ rect3d.isOversized = true;
+ rect3d.topLeft.y = (maxDimension - maxDisplaySize.height / 2) * -1;
+ rect3d.bottomRight.y = maxDimension;
+ }
+
+ return rect3d;
+ }
+
+ private computeLabels(rects2d: Rectangle[], rects3d: Rect3D[]): Label3D[] {
+ const labels3d: Label3D[] = [];
+
+ let labelY =
+ Math.max(
+ ...rects3d.map((rect) => {
+ return this.matMultiply(rect.transform, rect.bottomRight).y;
+ })
+ ) + Mapper3D.LABEL_FIRST_Y_OFFSET;
+
+ rects2d.forEach((rect2d, index) => {
+ if (!rect2d.label) {
+ return;
+ }
+
+ const rect3d = rects3d[index];
+
+ const bottomLeft: Point3D = {
+ x: rect3d.topLeft.x,
+ y: rect3d.bottomRight.y,
+ z: rect3d.topLeft.z,
+ };
+ const topRight: Point3D = {
+ x: rect3d.bottomRight.x,
+ y: rect3d.topLeft.y,
+ z: rect3d.bottomRight.z,
+ };
+ const lineStarts = [
+ this.matMultiply(rect3d.transform, rect3d.topLeft),
+ this.matMultiply(rect3d.transform, rect3d.bottomRight),
+ this.matMultiply(rect3d.transform, bottomLeft),
+ this.matMultiply(rect3d.transform, topRight),
+ ];
+ let maxIndex = 0;
+ for (let i = 1; i < lineStarts.length; i++) {
+ if (lineStarts[i].x > lineStarts[maxIndex].x) {
+ maxIndex = i;
+ }
+ }
+ const lineStart = lineStarts[maxIndex];
+ lineStart.x += Mapper3D.LABEL_CIRCLE_RADIUS / 2;
+
+ const lineEnd: Point3D = {
+ x: lineStart.x,
+ y: labelY,
+ z: lineStart.z,
+ };
+
+ const isHighlighted = this.highlightedRectIds.includes(rect2d.id);
+
+ const label3d: Label3D = {
+ circle: {
+ radius: Mapper3D.LABEL_CIRCLE_RADIUS,
+ center: {
+ x: lineStart.x,
+ y: lineStart.y,
+ z: lineStart.z + 0.5,
+ },
+ },
+ linePoints: [lineStart, lineEnd],
+ textCenter: lineEnd,
+ text: rect2d.label,
+ isHighlighted,
+ rectId: rect2d.id,
+ };
+ labels3d.push(label3d);
+
+ labelY += Mapper3D.LABEL_TEXT_Y_SPACING;
+ });
+
+ return labels3d;
+ }
+
+ private matMultiply(mat: Transform3D, point: Point3D): Point3D {
+ return {
+ x: mat.dsdx * point.x + mat.dsdy * point.y + mat.tx,
+ y: mat.dtdx * point.x + mat.dtdy * point.y + mat.ty,
+ z: point.z,
+ };
+ }
+
+ private computeBoundingBox(rects: Rect3D[], labels: Label3D[]): Box3D {
+ if (rects.length === 0) {
+ return {
+ width: 1,
+ height: 1,
+ depth: 1,
+ center: {x: 0, y: 0, z: 0},
+ diagonal: Math.sqrt(3),
+ };
+ }
+
+ let minX = Number.MAX_VALUE;
+ let maxX = Number.MIN_VALUE;
+ let minY = Number.MAX_VALUE;
+ let maxY = Number.MIN_VALUE;
+ let minZ = Number.MAX_VALUE;
+ let maxZ = Number.MIN_VALUE;
+
+ const updateMinMaxCoordinates = (point: Point3D) => {
+ minX = Math.min(minX, point.x);
+ maxX = Math.max(maxX, point.x);
+ minY = Math.min(minY, point.y);
+ maxY = Math.max(maxY, point.y);
+ minZ = Math.min(minZ, point.z);
+ maxZ = Math.max(maxZ, point.z);
+ };
+
+ rects.forEach((rect) => {
+ /*const topLeft: Point3D = {
+ x: rect.center.x - rect.width / 2,
+ y: rect.center.y + rect.height / 2,
+ z: rect.center.z
+ };
+ const bottomRight: Point3D = {
+ x: rect.center.x + rect.width / 2,
+ y: rect.center.y - rect.height / 2,
+ z: rect.center.z
+ };*/
+ updateMinMaxCoordinates(rect.topLeft);
+ updateMinMaxCoordinates(rect.bottomRight);
+ });
+
+ labels.forEach((label) => {
+ label.linePoints.forEach((point) => {
+ updateMinMaxCoordinates(point);
+ });
+ });
+
+ const center: Point3D = {
+ x: (minX + maxX) / 2,
+ y: (minY + maxY) / 2,
+ z: (minZ + maxZ) / 2,
+ };
+
+ const width = maxX - minX;
+ const height = maxY - minY;
+ const depth = maxZ - minZ;
+
+ return {
+ width,
+ height,
+ depth,
+ center,
+ diagonal: Math.sqrt(width * width + height * height + depth * depth),
+ };
+ }
+}
+
+export {Mapper3D};
diff --git a/tools/winscope/src/viewers/components/rects/rects_component.ts b/tools/winscope/src/viewers/components/rects/rects_component.ts
new file mode 100644
index 0000000..83dc6c1
--- /dev/null
+++ b/tools/winscope/src/viewers/components/rects/rects_component.ts
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2022 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 {Component, ElementRef, HostListener, Inject, Input, OnDestroy, OnInit} from '@angular/core';
+import {Rectangle} from 'viewers/common/rectangle';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+import {Canvas} from './canvas';
+import {Mapper3D} from './mapper3d';
+import {Distance2D} from './types3d';
+
+@Component({
+ selector: 'rects-view',
+ template: `
+ <div class="view-controls">
+ <h2 class="mat-title">{{ title }}</h2>
+ <div class="top-view-controls">
+ <mat-checkbox
+ color="primary"
+ [checked]="mapper3d.getShowOnlyVisibleMode()"
+ (change)="onShowOnlyVisibleModeChange($event.checked!)"
+ >Only visible
+ </mat-checkbox>
+ <mat-checkbox
+ color="primary"
+ [disabled]="mapper3d.getShowOnlyVisibleMode()"
+ [checked]="mapper3d.getShowVirtualMode()"
+ (change)="onShowVirtualModeChange($event.checked!)"
+ >Show virtual
+ </mat-checkbox>
+ <div class="right-btn-container">
+ <button color="primary" mat-icon-button (click)="onZoomInClick()">
+ <mat-icon aria-hidden="true"> zoom_in </mat-icon>
+ </button>
+ <button color="primary" mat-icon-button (click)="onZoomOutClick()">
+ <mat-icon aria-hidden="true"> zoom_out </mat-icon>
+ </button>
+ <button
+ color="primary"
+ mat-icon-button
+ matTooltip="Restore camera settings"
+ (click)="resetCamera()">
+ <mat-icon aria-hidden="true"> restore </mat-icon>
+ </button>
+ </div>
+ </div>
+ <div class="slider-view-controls">
+ <div class="slider-container">
+ <p class="slider-label mat-body-2">Rotation</p>
+ <mat-slider
+ class="slider-rotation"
+ step="0.02"
+ min="0"
+ max="1"
+ aria-label="units"
+ [value]="mapper3d.getCameraRotationFactor()"
+ (input)="onRotationSliderChange($event.value!)"
+ color="primary"></mat-slider>
+ </div>
+ <div class="slider-container">
+ <p class="slider-label mat-body-2">Spacing</p>
+ <mat-slider
+ class="slider-spacing"
+ step="0.02"
+ min="0.02"
+ max="1"
+ aria-label="units"
+ [value]="mapper3d.getZSpacingFactor()"
+ (input)="onSeparationSliderChange($event.value!)"
+ color="primary"></mat-slider>
+ </div>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="rects-content">
+ <div class="canvas-container">
+ <canvas class="canvas-rects" (click)="onRectClick($event)" oncontextmenu="return false">
+ </canvas>
+ <div class="canvas-labels"></div>
+ </div>
+ <div *ngIf="internalDisplayIds.length > 1" class="display-button-container">
+ <button
+ *ngFor="let displayId of internalDisplayIds"
+ color="primary"
+ mat-raised-button
+ (click)="onDisplayIdChange(displayId)">
+ {{ displayId }}
+ </button>
+ </div>
+ </div>
+ `,
+ styles: [
+ `
+ .view-controls {
+ display: flex;
+ flex-direction: column;
+ }
+ .top-view-controls,
+ .slider-view-controls {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ column-gap: 10px;
+ align-items: center;
+ margin-bottom: 12px;
+ }
+ .right-btn-container {
+ margin-left: auto;
+ }
+ .slider-view-controls {
+ justify-content: space-between;
+ }
+ .slider-container {
+ position: relative;
+ }
+ .slider-label {
+ position: absolute;
+ top: 0;
+ }
+ .rects-content {
+ height: 100%;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+ .canvas-container {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ }
+ .canvas-rects {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ }
+ .canvas-labels {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ }
+ .display-button-container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ column-gap: 10px;
+ }
+ `,
+ ],
+})
+export class RectsComponent implements OnInit, OnDestroy {
+ @Input() title = 'title';
+ @Input() set rects(rects: Rectangle[]) {
+ this.internalRects = rects;
+ this.drawScene();
+ }
+
+ @Input() set displayIds(ids: number[]) {
+ this.internalDisplayIds = ids;
+ if (!this.internalDisplayIds.includes(this.mapper3d.getCurrentDisplayId())) {
+ this.mapper3d.setCurrentDisplayId(this.internalDisplayIds[0]);
+ this.drawScene();
+ }
+ }
+
+ @Input() set highlightedItems(stableIds: string[]) {
+ this.internalHighlightedItems = stableIds;
+ this.mapper3d.setHighlightedRectIds(this.internalHighlightedItems);
+ this.drawScene();
+ }
+
+ private internalRects: Rectangle[] = [];
+ private internalDisplayIds: number[] = [];
+ private internalHighlightedItems: string[] = [];
+
+ private mapper3d: Mapper3D;
+ private canvas?: Canvas;
+ private resizeObserver: ResizeObserver;
+ private canvasRects?: HTMLCanvasElement;
+ private canvasLabels?: HTMLElement;
+ private mouseMoveListener = (event: MouseEvent) => this.onMouseMove(event);
+ private mouseUpListener = (event: MouseEvent) => this.onMouseUp(event);
+
+ constructor(@Inject(ElementRef) private elementRef: ElementRef) {
+ this.mapper3d = new Mapper3D();
+ this.resizeObserver = new ResizeObserver((entries) => {
+ this.drawScene();
+ });
+ }
+
+ ngOnInit() {
+ const canvasContainer = this.elementRef.nativeElement.querySelector('.canvas-container');
+ this.resizeObserver.observe(canvasContainer);
+
+ this.canvasRects = canvasContainer.querySelector('.canvas-rects')! as HTMLCanvasElement;
+ this.canvasLabels = canvasContainer.querySelector('.canvas-labels');
+ this.canvas = new Canvas(this.canvasRects, this.canvasLabels!);
+
+ this.canvasRects.addEventListener('mousedown', (event) => this.onCanvasMouseDown(event));
+
+ this.mapper3d.setCurrentDisplayId(this.internalDisplayIds[0] ?? 0);
+ this.drawScene();
+ }
+
+ ngOnDestroy() {
+ this.resizeObserver?.disconnect();
+ }
+
+ onSeparationSliderChange(factor: number) {
+ this.mapper3d.setZSpacingFactor(factor);
+ this.drawScene();
+ }
+
+ onRotationSliderChange(factor: number) {
+ this.mapper3d.setCameraRotationFactor(factor);
+ this.drawScene();
+ }
+
+ resetCamera() {
+ this.mapper3d.resetCamera();
+ this.drawScene();
+ }
+
+ @HostListener('wheel', ['$event'])
+ onScroll(event: WheelEvent) {
+ if (event.deltaY > 0) {
+ this.doZoomOut();
+ } else {
+ this.doZoomIn();
+ }
+ }
+
+ onCanvasMouseDown(event: MouseEvent) {
+ document.addEventListener('mousemove', this.mouseMoveListener);
+ document.addEventListener('mouseup', this.mouseUpListener);
+ }
+
+ onMouseMove(event: MouseEvent) {
+ const distance = new Distance2D(event.movementX, event.movementY);
+ this.mapper3d.addPanScreenDistance(distance);
+ this.drawScene();
+ }
+
+ onMouseUp(event: MouseEvent) {
+ document.removeEventListener('mousemove', this.mouseMoveListener);
+ document.removeEventListener('mouseup', this.mouseUpListener);
+ }
+
+ onZoomInClick() {
+ this.doZoomIn();
+ }
+
+ onZoomOutClick() {
+ this.doZoomOut();
+ }
+
+ onShowOnlyVisibleModeChange(enabled: boolean) {
+ this.mapper3d.setShowOnlyVisibleMode(enabled);
+ this.drawScene();
+ }
+
+ onShowVirtualModeChange(enabled: boolean) {
+ this.mapper3d.setShowVirtualMode(enabled);
+ this.drawScene();
+ }
+
+ onDisplayIdChange(id: number) {
+ this.mapper3d.setCurrentDisplayId(id);
+ this.drawScene();
+ }
+
+ onRectClick(event: MouseEvent) {
+ event.preventDefault();
+
+ const canvas = event.target as Element;
+ const canvasOffset = canvas.getBoundingClientRect();
+
+ const x = ((event.clientX - canvasOffset.left) / canvas.clientWidth) * 2 - 1;
+ const y = -((event.clientY - canvasOffset.top) / canvas.clientHeight) * 2 + 1;
+ const z = 0;
+
+ const id = this.canvas?.getClickedRectId(x, y, z);
+ if (id !== undefined) {
+ this.notifyHighlightedItem(id);
+ }
+ }
+
+ private doZoomIn() {
+ this.mapper3d.increaseZoomFactor();
+ this.drawScene();
+ }
+
+ private doZoomOut() {
+ this.mapper3d.decreaseZoomFactor();
+ this.drawScene();
+ }
+
+ private drawScene() {
+ // TODO: Re-create scene only when input rects change. With the other input events
+ // (rotation, spacing, ...) we can just update the camera and/or update the mesh positions.
+ // We'd probably need to get rid of the intermediate layer (Scene3D, Rect3D, ... types) and
+ // work directly with three.js's meshes.
+ this.mapper3d.setRects(this.internalRects);
+ this.canvas?.draw(this.mapper3d.computeScene());
+ }
+
+ private notifyHighlightedItem(id: string) {
+ const event: CustomEvent = new CustomEvent(ViewerEvents.HighlightedChange, {
+ bubbles: true,
+ detail: {id},
+ });
+ this.elementRef.nativeElement.dispatchEvent(event);
+ }
+}
diff --git a/tools/winscope/src/viewers/components/rects/rects_component_test.ts b/tools/winscope/src/viewers/components/rects/rects_component_test.ts
new file mode 100644
index 0000000..0196e99
--- /dev/null
+++ b/tools/winscope/src/viewers/components/rects/rects_component_test.ts
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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 {CommonModule} from '@angular/common';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatRadioModule} from '@angular/material/radio';
+import {MatSliderModule} from '@angular/material/slider';
+import {Rectangle} from 'viewers/common/rectangle';
+import {RectsComponent} from 'viewers/components/rects/rects_component';
+import {Canvas} from './canvas';
+
+describe('RectsComponent', () => {
+ let component: RectsComponent;
+ let fixture: ComponentFixture<RectsComponent>;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [CommonModule, MatCheckboxModule, MatDividerModule, MatSliderModule, MatRadioModule],
+ declarations: [RectsComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(RectsComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('renders rotation slider', () => {
+ const slider = htmlElement.querySelector('mat-slider.slider-rotation');
+ expect(slider).toBeTruthy();
+ });
+
+ it('renders separation slider', () => {
+ const slider = htmlElement.querySelector('mat-slider.slider-spacing');
+ expect(slider).toBeTruthy();
+ });
+
+ it('renders canvas', () => {
+ const rectsCanvas = htmlElement.querySelector('.canvas-rects');
+ expect(rectsCanvas).toBeTruthy();
+ });
+
+ it('draws scene when input data changes', async () => {
+ spyOn(Canvas.prototype, 'draw').and.callThrough();
+
+ const inputRect: Rectangle = {
+ topLeft: {x: 0, y: 0},
+ bottomRight: {x: 1, y: -1},
+ label: 'rectangle1',
+ transform: {
+ matrix: {
+ dsdx: 1,
+ dsdy: 0,
+ dtdx: 0,
+ dtdy: 1,
+ tx: 0,
+ ty: 0,
+ },
+ },
+ isVisible: true,
+ isDisplay: false,
+ ref: null,
+ id: 'test-id-1234',
+ displayId: 0,
+ isVirtual: false,
+ isClickable: false,
+ cornerRadius: 0,
+ };
+
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(0);
+ component.rects = [inputRect];
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(1);
+ component.rects = [inputRect];
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(2);
+ });
+
+ it('draws scene when rotation slider changes', () => {
+ spyOn(Canvas.prototype, 'draw').and.callThrough();
+ const slider = htmlElement.querySelector('.slider-rotation');
+
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(0);
+
+ slider?.dispatchEvent(new MouseEvent('mousedown'));
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(1);
+ });
+
+ it('draws scene when spacing slider changes', () => {
+ spyOn(Canvas.prototype, 'draw').and.callThrough();
+ const slider = htmlElement.querySelector('.slider-spacing');
+
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(0);
+
+ slider?.dispatchEvent(new MouseEvent('mousedown'));
+ expect(Canvas.prototype.draw).toHaveBeenCalledTimes(1);
+ });
+
+ it('draws display buttons', () => {
+ component.displayIds = [0, 1, 2];
+
+ fixture.detectChanges();
+
+ const displayButtonContainer = htmlElement.querySelector('.display-button-container');
+ expect(displayButtonContainer).toBeTruthy();
+
+ const buttons = Array.from(displayButtonContainer?.querySelectorAll('button') ?? []);
+ expect(buttons.length).toBe(3);
+
+ const buttonValues = buttons.map((it) => it.textContent?.trim());
+ expect(buttonValues).toEqual(['0', '1', '2']);
+ });
+});
diff --git a/tools/winscope/src/viewers/components/rects/types3d.ts b/tools/winscope/src/viewers/components/rects/types3d.ts
new file mode 100644
index 0000000..cb30635
--- /dev/null
+++ b/tools/winscope/src/viewers/components/rects/types3d.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 enum ColorType {
+ VISIBLE,
+ NOT_VISIBLE,
+ HIGHLIGHTED,
+}
+
+export class Distance2D {
+ constructor(public dx: number, public dy: number) {}
+}
+
+export interface Box3D {
+ width: number;
+ height: number;
+ depth: number;
+ center: Point3D;
+ diagonal: number;
+}
+
+export interface Rect3D {
+ id: string;
+ topLeft: Point3D;
+ bottomRight: Point3D;
+ cornerRadius: number;
+ darkFactor: number;
+ colorType: ColorType;
+ isClickable: boolean;
+ transform: Transform3D;
+ isOversized: boolean;
+}
+
+export interface Transform3D {
+ dsdx: number;
+ dsdy: number;
+ tx: number;
+ dtdx: number;
+ dtdy: number;
+ ty: number;
+}
+
+export interface Point3D {
+ x: number;
+ y: number;
+ z: number;
+}
+
+export interface Label3D {
+ circle: Circle3D;
+ linePoints: Point3D[];
+ textCenter: Point3D;
+ text: string;
+ isHighlighted: boolean;
+ rectId: string;
+}
+
+export interface Circle3D {
+ radius: number;
+ center: Point3D;
+}
+
+export interface Scene3D {
+ boundingBox: Box3D;
+ camera: Camera;
+ rects: Rect3D[];
+ labels: Label3D[];
+}
+
+export interface Camera {
+ rotationFactor: number;
+ zoomFactor: number;
+ panScreenDistance: Distance2D;
+}
diff --git a/tools/winscope/src/viewers/components/styles/node.styles.ts b/tools/winscope/src/viewers/components/styles/node.styles.ts
new file mode 100644
index 0000000..f8d8f9b
--- /dev/null
+++ b/tools/winscope/src/viewers/components/styles/node.styles.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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 const nodeStyles = `
+ .node {position: relative;display: inline-flex;padding: 2px 0; width: 100%;}
+ .node.clickable {cursor: pointer;}
+ .node:not(.selected).added,
+ .node:not(.selected).addedMove {
+ background: #03ff35;
+ }
+
+ .node:not(.selected).deleted,
+ .node:not(.selected).deletedMove {
+ background: #ff6b6b;
+ }
+
+ .node:hover:not(.selected) {background-color: rgba(127, 127, 127, 0.5)}
+
+ .node:not(.selected).modified {
+ background: cyan;
+ }
+
+ .node.addedMove:after,
+ .node.deletedMove:after {
+ content: 'moved';
+ margin: 0 5px;
+ background: #448aff;
+ border-radius: 5px;
+ padding: 3px;
+ color: white;
+ }
+
+ .selected {background-color: #365179;color: white;}
+`;
+
+// FIXME: child-hover selector is not working.
+export const treeNodeDataViewStyles = `
+ .node + .children:not(.flattened) {margin-left: 12px;padding-left: 11px;border-left: 1px solid var(--border-color);}
+ .node.selected + .children {border-left: 1px solid rgb(150, 150, 150);}
+ .node:hover + .children {border-left: 1px solid rgba(150, 150, 150, 0.75);}
+ .node.child-hover + .children {border-left: 1px solid #b4b4b4;}
+`;
+
+export const nodeInnerItemStyles = `
+ .leaf-node-icon {content: ''; display: inline-block; margin-left: 40%; margin-top: 40%; height: 5px; width: 5px; border-radius: 50%;background-color: #9b9b9b;}
+ .leaf-node-icon-wrapper, .description, .toggle-tree-btn, .expand-tree-btn, .pin-node-btn { position: relative; display: inline-block;}
+ .pin-node-btn {padding: 0; transform: scale(0.7)}
+ .description {align-items: center; flex: 1 1 auto; vertical-align: middle; word-break: break-all; flex-basis: 0;}
+ .leaf-node-icon-wrapper{padding-left: 6px; padding-right: 6px; min-height: 24px; width: 24px; position:relative; align-content: center; vertical-align: middle;}
+
+ .icon-button {
+ background: none;
+ border: none;
+ display: inline-block;
+ vertical-align: middle;
+ color: inherit;
+ }
+
+ .expand-tree-btn {
+ float: right;
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .expand-tree-btn.modified {
+ background: cyan;
+ }
+
+ .expand-tree-btn.deleted,
+ .expand-tree-btn.deletedMove {
+ background: #ff6b6b;
+ }
+
+ .expand-tree-btn.added,
+ .expand-tree-btn.addedMove {
+ background: #03ff35;
+ }
+`;
diff --git a/tools/winscope/src/viewers/components/styles/tree_node_data_view.styles.ts b/tools/winscope/src/viewers/components/styles/tree_node_data_view.styles.ts
new file mode 100644
index 0000000..03e7251
--- /dev/null
+++ b/tools/winscope/src/viewers/components/styles/tree_node_data_view.styles.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 const treeNodeDataViewStyles = `
+ .tree-view-internal-chip {
+ display: inline-block;
+ }
+
+ .tree-view-chip {
+ margin: 0 5px;
+ padding: 0 10px;
+ border-radius: 10px;
+ background-color: #aaa;
+ color: black;
+ }
+
+ .tree-view-chip.tree-view-chip-warn {
+ background-color: #ffaa6b;
+ }
+
+ .tree-view-chip.tree-view-chip-error {
+ background-color: #ff6b6b;
+ }
+
+ .tree-view-chip.tree-view-chip-gpu {
+ background-color: #00c853;
+ }
+
+ .tree-view-chip.tree-view-chip-hwc {
+ background-color: #448aff;
+ }
+`;
+
+export const treeNodePropertiesDataViewStyles = `
+ .value {
+ color: #8A2BE2;
+ }
+ .value.null {
+ color: #e1e1e1;
+ }
+ .value.number {
+ color: #4c75fd;
+ }
+ .value.true {
+ color: #2ECC40;
+ }
+ .value.false {
+ color: #FF4136;
+ }
+`;
diff --git a/tools/winscope/src/viewers/components/transform_matrix_component.ts b/tools/winscope/src/viewers/components/transform_matrix_component.ts
new file mode 100644
index 0000000..36959cc
--- /dev/null
+++ b/tools/winscope/src/viewers/components/transform_matrix_component.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {Transform} from 'trace/flickerlib/common';
+
+@Component({
+ selector: 'transform-matrix',
+ template: `
+ <div *ngIf="transform" class="matrix" [matTooltip]="transform.getTypeAsString()">
+ <p class="mat-body-1">
+ {{ formatFloat(transform.matrix.dsdx) }}
+ </p>
+ <p class="mat-body-1">
+ {{ formatFloat(transform.matrix.dsdy) }}
+ </p>
+ <p class="mat-body-1" matTooltip="Translate x">
+ {{ formatFloat(transform.matrix.tx) }}
+ </p>
+
+ <p class="mat-body-1">
+ {{ formatFloat(transform.matrix.dtdx) }}
+ </p>
+ <p class="mat-body-1">
+ {{ formatFloat(transform.matrix.dtdy) }}
+ </p>
+ <p class="mat-body-1" matTooltip="Translate y">
+ {{ formatFloat(transform.matrix.ty) }}
+ </p>
+
+ <p class="mat-body-1">0</p>
+ <p class="mat-body-1">0</p>
+ <p class="mat-body-1">1</p>
+ </div>
+ `,
+ styles: [
+ `
+ .matrix {
+ display: grid;
+ grid-gap: 1px;
+ grid-template-columns: repeat(3, 1fr);
+ text-align: center;
+ }
+ `,
+ ],
+})
+export class TransformMatrixComponent {
+ @Input() transform!: Transform;
+ @Input() formatFloat!: (num: number) => number;
+}
diff --git a/tools/winscope/src/viewers/components/transform_matrix_component_test.ts b/tools/winscope/src/viewers/components/transform_matrix_component_test.ts
new file mode 100644
index 0000000..0b54b94
--- /dev/null
+++ b/tools/winscope/src/viewers/components/transform_matrix_component_test.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {TransformMatrixComponent} from './transform_matrix_component';
+
+describe('TransformMatrixComponent', () => {
+ let fixture: ComponentFixture<TransformMatrixComponent>;
+ let component: TransformMatrixComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [TransformMatrixComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TransformMatrixComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/tree_component.ts b/tools/winscope/src/viewers/components/tree_component.ts
new file mode 100644
index 0000000..af45c25
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_component.ts
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2022 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 {
+ ChangeDetectionStrategy,
+ Component,
+ ElementRef,
+ EventEmitter,
+ Inject,
+ Input,
+ Output,
+} from '@angular/core';
+import {PersistentStore} from 'common/persistent_store';
+import {TraceType} from 'trace/trace_type';
+import {HierarchyTreeNode, UiTreeNode, UiTreeUtils} from 'viewers/common/ui_tree_utils';
+import {nodeStyles, treeNodeDataViewStyles} from 'viewers/components/styles/node.styles';
+
+@Component({
+ selector: 'tree-view',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+ <tree-node
+ *ngIf="item && showNode(item)"
+ class="node"
+ [class.leaf]="isLeaf(this.item)"
+ [class.selected]="isHighlighted(item, highlightedItems)"
+ [class.clickable]="isClickable()"
+ [class.hover]="nodeHover"
+ [class.childHover]="childHover"
+ [isAlwaysCollapsed]="isAlwaysCollapsed"
+ [class]="diffClass(item)"
+ [style]="nodeOffsetStyle()"
+ [item]="item"
+ [flattened]="isFlattened"
+ [isLeaf]="isLeaf(this.item)"
+ [isCollapsed]="isAlwaysCollapsed ?? isCollapsed()"
+ [hasChildren]="hasChildren()"
+ [isPinned]="isPinned()"
+ (toggleTreeChange)="toggleTree()"
+ (click)="onNodeClick($event)"
+ (expandTreeChange)="expandTree()"
+ (pinNodeChange)="propagateNewPinnedItem($event)"></tree-node>
+
+ <div
+ *ngIf="hasChildren()"
+ class="children"
+ [class.flattened]="isFlattened"
+ [hidden]="!isCollapsed()">
+ <tree-view
+ *ngFor="let child of children(); trackBy: childTrackById"
+ class="childrenTree"
+ [item]="child"
+ [store]="store"
+ [showNode]="showNode"
+ [isLeaf]="isLeaf"
+ [dependencies]="dependencies"
+ [isFlattened]="isFlattened"
+ [useGlobalCollapsedState]="useGlobalCollapsedState"
+ [initialDepth]="initialDepth + 1"
+ [highlightedItems]="highlightedItems"
+ [pinnedItems]="pinnedItems"
+ (highlightedItemChange)="propagateNewHighlightedItem($event)"
+ (pinnedItemChange)="propagateNewPinnedItem($event)"
+ (selectedTreeChange)="propagateNewSelectedTree($event)"
+ [itemsClickable]="itemsClickable"
+ (hoverStart)="childHover = true"
+ (hoverEnd)="childHover = false"></tree-view>
+ </div>
+ `,
+ styles: [nodeStyles, treeNodeDataViewStyles],
+})
+export class TreeComponent {
+ diffClass = UiTreeUtils.diffClass;
+ isHighlighted = UiTreeUtils.isHighlighted;
+
+ // TODO (b/263779536): this array is passed down from viewers/presenters and is used to generate
+ // an identifier supposed to be unique for each viewer. Let's just use a proper identifier
+ // instead. Each viewer/presenter could pass down a random magic number, an UUID, ...
+ @Input() dependencies: TraceType[] = [];
+
+ @Input() item?: UiTreeNode;
+ @Input() store!: PersistentStore;
+ @Input() isFlattened? = false;
+ @Input() initialDepth = 0;
+ @Input() highlightedItems: string[] = [];
+ @Input() pinnedItems?: HierarchyTreeNode[] = [];
+ @Input() itemsClickable?: boolean;
+ @Input() useGlobalCollapsedState?: boolean;
+ @Input() isAlwaysCollapsed?: boolean;
+ @Input() showNode = (item: UiTreeNode) => true;
+ @Input() isLeaf = (item?: UiTreeNode) => {
+ return !item || !item.children || item.children.length === 0;
+ };
+
+ @Output() highlightedItemChange = new EventEmitter<string>();
+ @Output() selectedTreeChange = new EventEmitter<UiTreeNode>();
+ @Output() pinnedItemChange = new EventEmitter<UiTreeNode>();
+ @Output() hoverStart = new EventEmitter<void>();
+ @Output() hoverEnd = new EventEmitter<void>();
+
+ isCollapsedByDefault = true;
+ localCollapsedState = this.isCollapsedByDefault;
+ nodeHover = false;
+ childHover = false;
+ readonly levelOffset = 24;
+ nodeElement: HTMLElement;
+
+ childTrackById(index: number, child: UiTreeNode): string {
+ if (child.stableId !== undefined) {
+ return child.stableId;
+ }
+ if (!(child instanceof HierarchyTreeNode) && typeof child.propertyKey === 'string') {
+ return child.propertyKey;
+ }
+
+ throw Error('Missing stable id or property key on node');
+ }
+
+ constructor(@Inject(ElementRef) public elementRef: ElementRef) {
+ this.nodeElement = elementRef.nativeElement.querySelector('.node');
+ this.nodeElement?.addEventListener('mousedown', this.nodeMouseDownEventListener);
+ this.nodeElement?.addEventListener('mouseenter', this.nodeMouseEnterEventListener);
+ this.nodeElement?.addEventListener('mouseleave', this.nodeMouseLeaveEventListener);
+ }
+
+ ngOnInit() {
+ if (this.isCollapsedByDefault) {
+ this.setCollapseValue(this.isCollapsedByDefault);
+ }
+ }
+
+ ngOnChanges() {
+ if (
+ this.item instanceof HierarchyTreeNode &&
+ UiTreeUtils.isHighlighted(this.item, this.highlightedItems)
+ ) {
+ this.selectedTreeChange.emit(this.item);
+ }
+ }
+
+ ngOnDestroy() {
+ this.nodeElement?.removeEventListener('mousedown', this.nodeMouseDownEventListener);
+ this.nodeElement?.removeEventListener('mouseenter', this.nodeMouseEnterEventListener);
+ this.nodeElement?.removeEventListener('mouseleave', this.nodeMouseLeaveEventListener);
+ }
+
+ onNodeClick(event: MouseEvent) {
+ event.preventDefault();
+ if (window.getSelection()?.type === 'range') {
+ return;
+ }
+
+ const isDoubleClick = event.detail % 2 === 0;
+ if (!this.isLeaf(this.item) && isDoubleClick) {
+ event.preventDefault();
+ this.toggleTree();
+ } else {
+ this.updateHighlightedItems();
+ }
+ }
+
+ nodeOffsetStyle() {
+ const offset = this.levelOffset * this.initialDepth + 'px';
+
+ return {
+ marginLeft: '-' + offset,
+ paddingLeft: offset,
+ };
+ }
+
+ private updateHighlightedItems() {
+ if (this.item?.stableId) {
+ this.highlightedItemChange.emit(`${this.item.stableId}`);
+ }
+ }
+
+ isPinned() {
+ if (this.item instanceof HierarchyTreeNode) {
+ return this.pinnedItems?.map((item) => `${item.stableId}`).includes(`${this.item.stableId}`);
+ }
+ return false;
+ }
+
+ propagateNewHighlightedItem(newId: string) {
+ this.highlightedItemChange.emit(newId);
+ }
+
+ propagateNewPinnedItem(newPinnedItem: UiTreeNode) {
+ this.pinnedItemChange.emit(newPinnedItem);
+ }
+
+ propagateNewSelectedTree(newTree: UiTreeNode) {
+ this.selectedTreeChange.emit(newTree);
+ }
+
+ isClickable() {
+ return !this.isLeaf(this.item) || this.itemsClickable;
+ }
+
+ toggleTree() {
+ this.setCollapseValue(!this.isCollapsed());
+ }
+
+ expandTree() {
+ this.setCollapseValue(true);
+ }
+
+ isCollapsed() {
+ if (this.isAlwaysCollapsed || this.isLeaf(this.item)) {
+ return true;
+ }
+
+ if (this.useGlobalCollapsedState) {
+ return (
+ this.store.get(`collapsedState.item.${this.dependencies}.${this.item?.stableId}`) ===
+ 'true' ?? this.isCollapsedByDefault
+ );
+ }
+ return this.localCollapsedState;
+ }
+
+ children(): UiTreeNode[] {
+ return this.item?.children ?? [];
+ }
+
+ hasChildren() {
+ if (!this.item) {
+ return false;
+ }
+ const isParentEntryInFlatView =
+ UiTreeUtils.isParentNode(this.item.kind ?? '') && this.isFlattened;
+ return (!this.isFlattened || isParentEntryInFlatView) && !this.isLeaf(this.item);
+ }
+
+ private setCollapseValue(isCollapsed: boolean) {
+ if (this.useGlobalCollapsedState) {
+ this.store.add(
+ `collapsedState.item.${this.dependencies}.${this.item?.stableId}`,
+ `${isCollapsed}`
+ );
+ } else {
+ this.localCollapsedState = isCollapsed;
+ }
+ }
+
+ private nodeMouseDownEventListener = (event: MouseEvent) => {
+ if (event.detail > 1) {
+ event.preventDefault();
+ return false;
+ }
+ return true;
+ };
+
+ private nodeMouseEnterEventListener = () => {
+ this.nodeHover = true;
+ this.hoverStart.emit();
+ };
+
+ private nodeMouseLeaveEventListener = () => {
+ this.nodeHover = false;
+ this.hoverEnd.emit();
+ };
+}
diff --git a/tools/winscope/src/viewers/components/tree_component_test.ts b/tools/winscope/src/viewers/components/tree_component_test.ts
new file mode 100644
index 0000000..2c568bc
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_component_test.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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 {Component, NO_ERRORS_SCHEMA, ViewChild} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {PersistentStore} from 'common/persistent_store';
+import {UiTreeNode} from 'viewers/common/ui_tree_utils';
+import {TreeComponent} from './tree_component';
+
+describe('TreeComponent', () => {
+ let fixture: ComponentFixture<TestHostComponent>;
+ let component: TestHostComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [TreeComponent, TestHostComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestHostComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ @Component({
+ selector: 'host-component',
+ template: `
+ <tree-view
+ [item]="item"
+ [store]="store"
+ [isFlattened]="isFlattened"
+ [diffClass]="diffClass"
+ [isHighlighted]="isHighlighted"
+ [hasChildren]="hasChildren"></tree-view>
+ `,
+ })
+ class TestHostComponent {
+ isFlattened = true;
+ item: UiTreeNode = {
+ simplifyNames: false,
+ kind: 'entry',
+ name: 'LayerTraceEntry',
+ shortName: 'LTE',
+ chips: [],
+ children: [{kind: '3', stableId: '3', name: 'Child1'}],
+ };
+ store = new PersistentStore();
+ diffClass = jasmine.createSpy().and.returnValue('none');
+ isHighlighted = jasmine.createSpy().and.returnValue(false);
+ hasChildren = jasmine.createSpy().and.returnValue(true);
+
+ @ViewChild(TreeComponent)
+ treeComponent!: TreeComponent;
+ }
+});
diff --git a/tools/winscope/src/viewers/components/tree_node_component.ts b/tools/winscope/src/viewers/components/tree_node_component.ts
new file mode 100644
index 0000000..fc88c36
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_node_component.ts
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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 {Component, EventEmitter, Input, Output} from '@angular/core';
+import {DiffType, HierarchyTreeNode, UiTreeNode, UiTreeUtils} from 'viewers/common/ui_tree_utils';
+import {nodeInnerItemStyles} from 'viewers/components/styles/node.styles';
+
+@Component({
+ selector: 'tree-node',
+ template: `
+ <button *ngIf="showChevron()" class="icon-button toggle-tree-btn" (click)="toggleTree($event)">
+ <mat-icon>
+ {{ isCollapsed ? 'arrow_drop_down' : 'chevron_right' }}
+ </mat-icon>
+ </button>
+
+ <div *ngIf="showLeafNodeIcon()" class="leaf-node-icon-wrapper">
+ <mat-icon class="leaf-node-icon"></mat-icon>
+ </div>
+
+ <button *ngIf="showPinNodeIcon()" class="icon-button pin-node-btn" (click)="pinNode($event)">
+ <mat-icon>
+ {{ isPinned ? 'star' : 'star_border' }}
+ </mat-icon>
+ </button>
+
+ <div class="description">
+ <tree-node-data-view *ngIf="!isPropertiesTreeNode()" [item]="item"></tree-node-data-view>
+ <tree-node-properties-data-view
+ *ngIf="isPropertiesTreeNode()"
+ [item]="item"></tree-node-properties-data-view>
+ </div>
+
+ <button
+ *ngIf="hasChildren && !isCollapsed"
+ class="icon-button expand-tree-btn"
+ [class]="collapseDiffClass"
+ (click)="expandTree($event)">
+ <mat-icon aria-hidden="true"> more_horiz </mat-icon>
+ </button>
+ `,
+ styles: [nodeInnerItemStyles],
+})
+export class TreeNodeComponent {
+ @Input() item!: UiTreeNode;
+ @Input() isLeaf?: boolean;
+ @Input() flattened?: boolean;
+ @Input() isCollapsed?: boolean;
+ @Input() hasChildren?: boolean = false;
+ @Input() isPinned?: boolean = false;
+ @Input() isInPinnedSection?: boolean = false;
+ @Input() isAlwaysCollapsed?: boolean;
+
+ @Output() toggleTreeChange = new EventEmitter<void>();
+ @Output() expandTreeChange = new EventEmitter<boolean>();
+ @Output() pinNodeChange = new EventEmitter<UiTreeNode>();
+
+ collapseDiffClass = '';
+
+ ngOnChanges() {
+ this.collapseDiffClass = this.updateCollapseDiffClass();
+ }
+
+ isPropertiesTreeNode() {
+ return !(this.item instanceof HierarchyTreeNode);
+ }
+
+ showPinNodeIcon() {
+ return (
+ (!this.isPropertiesTreeNode() && !UiTreeUtils.isParentNode(this.item.kind ?? '')) ?? false
+ );
+ }
+
+ toggleTree(event: MouseEvent) {
+ if (!this.isAlwaysCollapsed) {
+ event.stopPropagation();
+ this.toggleTreeChange.emit();
+ }
+ }
+
+ showChevron() {
+ return !this.isLeaf && !this.flattened && !this.isInPinnedSection;
+ }
+
+ showLeafNodeIcon() {
+ return !this.showChevron() && !this.isInPinnedSection;
+ }
+
+ expandTree(event: MouseEvent) {
+ event.stopPropagation();
+ this.expandTreeChange.emit();
+ }
+
+ pinNode(event: MouseEvent) {
+ event.stopPropagation();
+ this.pinNodeChange.emit(this.item);
+ }
+
+ updateCollapseDiffClass() {
+ 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 diffType = childrenDiffClasses.values().next().value;
+ return diffType;
+ }
+ return DiffType.MODIFIED;
+ }
+
+ private getAllDiffTypesOfChildren(item: UiTreeNode) {
+ if (!item.children) {
+ return new Set();
+ }
+
+ const classes = new Set();
+ for (const child of item.children) {
+ if (child.diffType) {
+ classes.add(child.diffType);
+ }
+ for (const diffClass of this.getAllDiffTypesOfChildren(child)) {
+ classes.add(diffClass);
+ }
+ }
+
+ return classes;
+ }
+}
diff --git a/tools/winscope/src/viewers/components/tree_node_component_test.ts b/tools/winscope/src/viewers/components/tree_node_component_test.ts
new file mode 100644
index 0000000..3854f7d
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_node_component_test.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 {Component, NO_ERRORS_SCHEMA, ViewChild} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {TreeNodeComponent} from './tree_node_component';
+
+describe('TreeNodeComponent', () => {
+ let fixture: ComponentFixture<TestHostComponent>;
+ let component: TestHostComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [TreeNodeComponent, TestHostComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestHostComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ @Component({
+ selector: 'host-component',
+ template: `
+ <tree-node
+ [item]="item"
+ [isCollapsed]="true"
+ [isPinned]="false"
+ [isInPinnedSection]="false"
+ [hasChildren]="false"></tree-node>
+ `,
+ })
+ class TestHostComponent {
+ item = {
+ simplifyNames: false,
+ kind: 'entry',
+ name: 'LayerTraceEntry',
+ shortName: 'LTE',
+ chips: [],
+ };
+
+ @ViewChild(TreeNodeComponent)
+ treeNodeComponent!: TreeNodeComponent;
+ }
+});
diff --git a/tools/winscope/src/viewers/components/tree_node_data_view_component.ts b/tools/winscope/src/viewers/components/tree_node_data_view_component.ts
new file mode 100644
index 0000000..a820762
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_node_data_view_component.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {Chip} from 'viewers/common/chip';
+import {HierarchyTreeNode, Terminal, UiTreeNode} from 'viewers/common/ui_tree_utils';
+import {treeNodeDataViewStyles} from 'viewers/components/styles/tree_node_data_view.styles';
+
+@Component({
+ selector: 'tree-node-data-view',
+ template: `
+ <span class="mat-body-1">
+ <span class="mat-body-2">{{ item.kind }}</span>
+ <ng-container *ngIf="item.kind && item.name">&ngsp;-&ngsp;</ng-container>
+ <span *ngIf="showShortName()" [matTooltip]="itemTooltip()">{{ itemShortName() }}</span>
+ <ng-container *ngIf="!showShortName()">{{ item.name }}</ng-container>
+ <div *ngFor="let chip of chips()" [class]="chipClass(chip)" [matTooltip]="chip.long">
+ {{ chip.short }}
+ </div>
+ </span>
+ `,
+ styles: [treeNodeDataViewStyles],
+})
+export class TreeNodeDataViewComponent {
+ @Input() item!: UiTreeNode;
+
+ chips() {
+ return this.item instanceof HierarchyTreeNode ? this.item.chips : [];
+ }
+
+ itemShortName() {
+ return this.item instanceof HierarchyTreeNode && this.item.shortName
+ ? this.item.shortName
+ : this.item.name;
+ }
+
+ itemTooltip() {
+ if (this.item.name instanceof Terminal) {
+ return '';
+ }
+ return this.item.name ?? '';
+ }
+
+ showShortName() {
+ return (
+ this.item instanceof HierarchyTreeNode &&
+ this.item.simplifyNames &&
+ this.item.shortName &&
+ this.item.shortName !== this.item.name
+ );
+ }
+
+ chipClass(chip: Chip) {
+ return [
+ 'tree-view-internal-chip',
+ 'tree-view-chip',
+ 'tree-view-chip' + '-' + (chip.type.toString() || 'default'),
+ ];
+ }
+}
diff --git a/tools/winscope/src/viewers/components/tree_node_data_view_component_test.ts b/tools/winscope/src/viewers/components/tree_node_data_view_component_test.ts
new file mode 100644
index 0000000..b802c74
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_node_data_view_component_test.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {TreeNodeDataViewComponent} from './tree_node_data_view_component';
+
+describe('TreeNodeDataViewComponent', () => {
+ let fixture: ComponentFixture<TreeNodeDataViewComponent>;
+ let component: TreeNodeDataViewComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [TreeNodeDataViewComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TreeNodeDataViewComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/tree_node_properties_data_view_component.ts b/tools/winscope/src/viewers/components/tree_node_properties_data_view_component.ts
new file mode 100644
index 0000000..9ea86fc
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_node_properties_data_view_component.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {treeNodePropertiesDataViewStyles} from 'viewers/components/styles/tree_node_data_view.styles';
+
+@Component({
+ selector: 'tree-node-properties-data-view',
+ template: `
+ <p class="mat-body-1">
+ {{ item.propertyKey }}
+ <ng-container *ngIf="item.propertyValue">
+ :&ngsp;
+ <span [class]="[valueClass()]" class="value">{{ item.propertyValue }}</span>
+ </ng-container>
+ </p>
+ `,
+ styles: [treeNodePropertiesDataViewStyles],
+})
+export class TreeNodePropertiesDataViewComponent {
+ @Input() item!: PropertiesTreeNode;
+
+ valueClass() {
+ if (!this.item.propertyValue) {
+ return null;
+ }
+
+ if (this.item.propertyValue === 'null') {
+ return 'null';
+ }
+
+ if (this.item.propertyValue === 'true') {
+ return 'true';
+ }
+
+ if (this.item.propertyValue === 'false') {
+ return 'false';
+ }
+
+ if (!isNaN(Number(this.item.propertyValue))) {
+ return 'number';
+ }
+
+ return null;
+ }
+}
diff --git a/tools/winscope/src/viewers/components/tree_node_properties_data_view_component_test.ts b/tools/winscope/src/viewers/components/tree_node_properties_data_view_component_test.ts
new file mode 100644
index 0000000..842f948
--- /dev/null
+++ b/tools/winscope/src/viewers/components/tree_node_properties_data_view_component_test.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {TreeNodePropertiesDataViewComponent} from './tree_node_properties_data_view_component';
+
+describe('TreeNodePropertiesDataViewComponent', () => {
+ let fixture: ComponentFixture<TreeNodePropertiesDataViewComponent>;
+ let component: TreeNodePropertiesDataViewComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ declarations: [TreeNodePropertiesDataViewComponent],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TreeNodePropertiesDataViewComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/components/viewer_input_method_component.ts b/tools/winscope/src/viewers/components/viewer_input_method_component.ts
new file mode 100644
index 0000000..d51813f
--- /dev/null
+++ b/tools/winscope/src/viewers/components/viewer_input_method_component.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {TRACE_INFO} from 'app/trace_info';
+import {PersistentStore} from 'common/persistent_store';
+import {TraceType} from 'trace/trace_type';
+import {ImeUiData} from 'viewers/common/ime_ui_data';
+
+@Component({
+ selector: 'viewer-input-method',
+ template: `
+ <div class="card-grid">
+ <div class="left-views">
+ <hierarchy-view
+ class="hierarchy-view"
+ [tree]="inputData?.tree ?? null"
+ [dependencies]="inputData?.dependencies ?? []"
+ [highlightedItems]="inputData?.highlightedItems ?? []"
+ [pinnedItems]="inputData?.pinnedItems ?? []"
+ [tableProperties]="inputData?.hierarchyTableProperties"
+ [store]="store"
+ [userOptions]="inputData?.hierarchyUserOptions ?? {}"></hierarchy-view>
+
+ <ng-container *ngIf="inputData?.additionalProperties">
+ <mat-divider></mat-divider>
+
+ <ime-additional-properties
+ class="ime-additional-properties"
+ [additionalProperties]="inputData?.additionalProperties!"></ime-additional-properties>
+ </ng-container>
+ </div>
+
+ <mat-divider [vertical]="true"></mat-divider>
+
+ <properties-view
+ class="properties-view"
+ [userOptions]="inputData?.propertiesUserOptions ?? {}"
+ [propertiesTree]="inputData?.propertiesTree ?? {}"></properties-view>
+ </div>
+ `,
+ styles: [
+ `
+ .left-views {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .hierarchy-view,
+ .ime-additional-properties,
+ .properties-view {
+ flex: 1;
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ }
+ `,
+ ],
+})
+export class ViewerInputMethodComponent {
+ @Input() inputData: ImeUiData | null = null;
+ @Input() store: PersistentStore = new PersistentStore();
+ @Input() active = false;
+ TRACE_INFO = TRACE_INFO;
+ TraceType = TraceType;
+}
diff --git a/tools/winscope/src/viewers/components/viewer_input_method_component_test.ts b/tools/winscope/src/viewers/components/viewer_input_method_component_test.ts
new file mode 100644
index 0000000..59e10ec
--- /dev/null
+++ b/tools/winscope/src/viewers/components/viewer_input_method_component_test.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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 {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatIconModule} from '@angular/material/icon';
+import {HierarchyComponent} from 'viewers/components/hierarchy_component';
+import {PropertiesComponent} from 'viewers/components/properties_component';
+import {ViewerInputMethodComponent} from './viewer_input_method_component';
+
+describe('ViewerInputMethodComponent', () => {
+ let fixture: ComponentFixture<ViewerInputMethodComponent>;
+ let component: ViewerInputMethodComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [MatIconModule, MatDividerModule],
+ declarations: [ViewerInputMethodComponent, HierarchyComponent, PropertiesComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ViewerInputMethodComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('creates hierarchy view', () => {
+ const hierarchyView = htmlElement.querySelector('.hierarchy-view');
+ expect(hierarchyView).toBeTruthy();
+ });
+
+ it('creates properties view', () => {
+ const propertiesView = htmlElement.querySelector('.properties-view');
+ expect(propertiesView).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer.ts b/tools/winscope/src/viewers/viewer.ts
new file mode 100644
index 0000000..26560ee
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+
+enum ViewType {
+ TAB,
+ OVERLAY,
+}
+
+class View {
+ constructor(
+ public type: ViewType,
+ public dependencies: TraceType[],
+ public htmlElement: HTMLElement,
+ public title: string,
+ public traceType: TraceType
+ ) {}
+}
+
+interface Viewer {
+ onTracePositionUpdate(position: TracePosition): void;
+ getViews(): View[];
+ getDependencies(): TraceType[];
+}
+
+export {Viewer, View, ViewType};
diff --git a/tools/winscope/src/viewers/viewer_factory.ts b/tools/winscope/src/viewers/viewer_factory.ts
new file mode 100644
index 0000000..a8da67e
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_factory.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {Viewer} from './viewer';
+import {ViewerInputMethodClients} from './viewer_input_method_clients/viewer_input_method_clients';
+import {ViewerInputMethodManagerService} from './viewer_input_method_manager_service/viewer_input_method_manager_service';
+import {ViewerInputMethodService} from './viewer_input_method_service/viewer_input_method_service';
+import {ViewerProtoLog} from './viewer_protolog/viewer_protolog';
+import {ViewerScreenRecording} from './viewer_screen_recording/viewer_screen_recording';
+import {ViewerSurfaceFlinger} from './viewer_surface_flinger/viewer_surface_flinger';
+import {ViewerTransactions} from './viewer_transactions/viewer_transactions';
+import {ViewerTransitions} from './viewer_transitions/viewer_transitions';
+import {ViewerWindowManager} from './viewer_window_manager/viewer_window_manager';
+
+class ViewerFactory {
+ // Note:
+ // the final order of tabs/views in the UI corresponds the order of the
+ // respective viewers below
+ static readonly VIEWERS = [
+ ViewerSurfaceFlinger,
+ ViewerWindowManager,
+ ViewerInputMethodClients,
+ ViewerInputMethodManagerService,
+ ViewerInputMethodService,
+ ViewerTransactions,
+ ViewerProtoLog,
+ ViewerScreenRecording,
+ ViewerTransitions,
+ ];
+
+ createViewers(activeTraceTypes: Set<TraceType>, traces: Traces, storage: Storage): Viewer[] {
+ const viewers: Viewer[] = [];
+
+ for (const Viewer of ViewerFactory.VIEWERS) {
+ const areViewerDepsSatisfied = Viewer.DEPENDENCIES.every((traceType: TraceType) =>
+ activeTraceTypes.has(traceType)
+ );
+
+ if (areViewerDepsSatisfied) {
+ viewers.push(new Viewer(traces, storage));
+ }
+ }
+
+ return viewers;
+ }
+}
+
+export {ViewerFactory};
diff --git a/tools/winscope/src/viewers/viewer_input_method_clients/presenter_input_method_clients.ts b/tools/winscope/src/viewers/viewer_input_method_clients/presenter_input_method_clients.ts
new file mode 100644
index 0000000..eb2aec1
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_clients/presenter_input_method_clients.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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 {PresenterInputMethod} from 'viewers/common/presenter_input_method';
+
+export class PresenterInputMethodClients extends PresenterInputMethod {
+ protected updateHierarchyTableProperties() {
+ return {
+ ...new ImClientsTableProperties(
+ this.entry?.obj?.client?.inputMethodManager?.curId,
+ this.entry?.obj?.client?.editorInfo?.packageName
+ ),
+ };
+ }
+}
+
+class ImClientsTableProperties {
+ constructor(public inputMethodId: string | undefined, public packageName: string | undefined) {}
+}
diff --git a/tools/winscope/src/viewers/viewer_input_method_clients/presenter_input_method_clients_test.ts b/tools/winscope/src/viewers/viewer_input_method_clients/presenter_input_method_clients_test.ts
new file mode 100644
index 0000000..d80e2b6
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_clients/presenter_input_method_clients_test.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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 {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {TraceType} from 'trace/trace_type';
+import {executePresenterInputMethodTests} from 'viewers/common/presenter_input_method_test_utils';
+import {PresenterInputMethodClients} from './presenter_input_method_clients';
+
+describe('PresenterInputMethodClients', () => {
+ describe('PresenterInputMethod tests:', () => {
+ const selectedTree = new HierarchyTreeBuilder().setId('entry').setStableId('entry').build();
+
+ executePresenterInputMethodTests(
+ selectedTree,
+ 'elapsed',
+ [2, 1],
+ true,
+ PresenterInputMethodClients,
+ TraceType.INPUT_METHOD_CLIENTS
+ );
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts b/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts
new file mode 100644
index 0000000..e2ebb42
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {ViewerInputMethod} from 'viewers/common/viewer_input_method';
+import {View, ViewType} from 'viewers/viewer';
+import {PresenterInputMethodClients} from './presenter_input_method_clients';
+
+class ViewerInputMethodClients extends ViewerInputMethod {
+ override getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Input Method Clients',
+ TraceType.INPUT_METHOD_CLIENTS
+ ),
+ ];
+ }
+
+ override getDependencies(): TraceType[] {
+ return ViewerInputMethodClients.DEPENDENCIES;
+ }
+
+ override initialisePresenter(traces: Traces, storage: Storage): PresenterInputMethodClients {
+ return new PresenterInputMethodClients(
+ traces,
+ storage,
+ this.getDependencies(),
+ this.imeUiCallback
+ );
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_CLIENTS];
+}
+
+export {ViewerInputMethodClients};
diff --git a/tools/winscope/src/viewers/viewer_input_method_manager_service/presenter_input_method_manager_service.ts b/tools/winscope/src/viewers/viewer_input_method_manager_service/presenter_input_method_manager_service.ts
new file mode 100644
index 0000000..5b40e1c
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_manager_service/presenter_input_method_manager_service.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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 {TraceTreeNode} from 'trace/trace_tree_node';
+import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties';
+import {ImeUtils} from 'viewers/common/ime_utils';
+import {PresenterInputMethod} from 'viewers/common/presenter_input_method';
+
+export class PresenterInputMethodManagerService extends PresenterInputMethod {
+ protected updateHierarchyTableProperties() {
+ return {
+ ...new ImManagerServiceTableProperties(
+ this.entry?.obj?.inputMethodManagerService?.curMethodId,
+ this.entry?.obj?.inputMethodManagerService?.curFocusedWindowName,
+ this.entry?.obj?.inputMethodManagerService?.lastImeTargetWindowName,
+ this.entry?.obj?.inputMethodManagerService?.inputShown ?? false
+ ),
+ };
+ }
+
+ protected override getAdditionalProperties(
+ wmEntry: TraceTreeNode | undefined,
+ sfEntry: TraceTreeNode | undefined
+ ) {
+ return new ImeAdditionalProperties(
+ wmEntry ? ImeUtils.processWindowManagerTraceEntry(wmEntry) : undefined,
+ undefined
+ );
+ }
+}
+
+class ImManagerServiceTableProperties {
+ constructor(
+ public inputMethodId: string | undefined,
+ public curFocusedWindow: string | undefined,
+ public lastImeTargetWindow: string | undefined,
+ public inputShown: boolean
+ ) {}
+}
diff --git a/tools/winscope/src/viewers/viewer_input_method_manager_service/presenter_input_method_manager_service_test.ts b/tools/winscope/src/viewers/viewer_input_method_manager_service/presenter_input_method_manager_service_test.ts
new file mode 100644
index 0000000..553f110
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_manager_service/presenter_input_method_manager_service_test.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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 {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {TraceType} from 'trace/trace_type';
+import {executePresenterInputMethodTests} from 'viewers/common/presenter_input_method_test_utils';
+import {PresenterInputMethodManagerService} from './presenter_input_method_manager_service';
+
+describe('PresenterInputMethodManagerService', () => {
+ describe('PresenterInputMethod tests:', () => {
+ const selectedTree = new HierarchyTreeBuilder()
+ .setId('managerservice')
+ .setStableId('managerservice')
+ .build();
+
+ executePresenterInputMethodTests(
+ selectedTree,
+ 'cur',
+ [13, 8],
+ false,
+ PresenterInputMethodManagerService,
+ TraceType.INPUT_METHOD_MANAGER_SERVICE
+ );
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts b/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts
new file mode 100644
index 0000000..74a9ea6
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {ViewerInputMethod} from 'viewers/common/viewer_input_method';
+import {View, ViewType} from 'viewers/viewer';
+import {PresenterInputMethodManagerService} from './presenter_input_method_manager_service';
+
+class ViewerInputMethodManagerService extends ViewerInputMethod {
+ override getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Input Method Manager Service',
+ TraceType.INPUT_METHOD_MANAGER_SERVICE
+ ),
+ ];
+ }
+
+ override getDependencies(): TraceType[] {
+ return ViewerInputMethodManagerService.DEPENDENCIES;
+ }
+
+ override initialisePresenter(traces: Traces, storage: Storage) {
+ return new PresenterInputMethodManagerService(
+ traces,
+ storage,
+ this.getDependencies(),
+ this.imeUiCallback
+ );
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_MANAGER_SERVICE];
+}
+
+export {ViewerInputMethodManagerService};
diff --git a/tools/winscope/src/viewers/viewer_input_method_service/presenter_input_method_service.ts b/tools/winscope/src/viewers/viewer_input_method_service/presenter_input_method_service.ts
new file mode 100644
index 0000000..0d7ca83
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_service/presenter_input_method_service.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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 {PresenterInputMethod} from 'viewers/common/presenter_input_method';
+
+export class PresenterInputMethodService extends PresenterInputMethod {
+ protected updateHierarchyTableProperties() {
+ return {
+ ...new ImServiceTableProperties(
+ this.entry?.obj?.inputMethodService?.windowVisible ?? false,
+ this.entry?.obj?.inputMethodService?.decorViewVisible ?? false,
+ this.entry?.obj?.inputMethodService?.inputEditorInfo?.packageName
+ ),
+ };
+ }
+}
+
+class ImServiceTableProperties {
+ constructor(
+ public windowVisible: boolean,
+ public decorViewVisible: boolean,
+ public packageName: string | undefined
+ ) {}
+}
diff --git a/tools/winscope/src/viewers/viewer_input_method_service/presenter_input_method_service_test.ts b/tools/winscope/src/viewers/viewer_input_method_service/presenter_input_method_service_test.ts
new file mode 100644
index 0000000..872b8b6
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_service/presenter_input_method_service_test.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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 {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {TraceType} from 'trace/trace_type';
+import {executePresenterInputMethodTests} from 'viewers/common/presenter_input_method_test_utils';
+import {PresenterInputMethodService} from './presenter_input_method_service';
+
+describe('PresenterInputMethodService', () => {
+ describe('PresenterInputMethod tests:', () => {
+ const selectedTree = new HierarchyTreeBuilder().setId('service').setStableId('service').build();
+
+ executePresenterInputMethodTests(
+ selectedTree,
+ 'visib',
+ [13, 3],
+ true,
+ PresenterInputMethodService,
+ TraceType.INPUT_METHOD_SERVICE
+ );
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts b/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts
new file mode 100644
index 0000000..8bc064f
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TraceType} from 'trace/trace_type';
+import {ViewerInputMethod} from 'viewers/common/viewer_input_method';
+import {View, ViewType} from 'viewers/viewer';
+import {PresenterInputMethodService} from './presenter_input_method_service';
+
+class ViewerInputMethodService extends ViewerInputMethod {
+ override getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Input Method Service',
+ TraceType.INPUT_METHOD_SERVICE
+ ),
+ ];
+ }
+
+ override getDependencies(): TraceType[] {
+ return ViewerInputMethodService.DEPENDENCIES;
+ }
+
+ override initialisePresenter(traces: Traces, storage: Storage) {
+ return new PresenterInputMethodService(
+ traces,
+ storage,
+ this.getDependencies(),
+ this.imeUiCallback
+ );
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_SERVICE];
+}
+
+export {ViewerInputMethodService};
diff --git a/tools/winscope/src/viewers/viewer_protolog/events.ts b/tools/winscope/src/viewers/viewer_protolog/events.ts
new file mode 100644
index 0000000..4cbe3a7
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/events.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+class Events {
+ static LogLevelsFilterChanged = 'ViewerProtoLogEvent_LogLevelsFilterChanged';
+ static TagsFilterChanged = 'ViewerProtoLogEvent_TagsFilterChanged';
+ static SourceFilesFilterChanged = 'ViewerProtoLogEvent_SourceFilesFilterChanged';
+ static SearchStringFilterChanged = 'ViewerProtoLogEvent_SearchStringFilterChanged';
+}
+
+export {Events};
diff --git a/tools/winscope/src/viewers/viewer_protolog/presenter.ts b/tools/winscope/src/viewers/viewer_protolog/presenter.ts
new file mode 100644
index 0000000..f3129f0
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/presenter.ts
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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 {ArrayUtils} from 'common/array_utils';
+import {LogMessage} from 'trace/protolog';
+import {Trace, TraceEntry} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {assertDefined} from '../../common/assert_utils';
+import {UiData} from './ui_data';
+
+export class Presenter {
+ private readonly trace: Trace<LogMessage>;
+ private readonly notifyUiDataCallback: (data: UiData) => void;
+ private entry?: TraceEntry<LogMessage>;
+ private originalIndicesOfFilteredOutputMessages: number[];
+ private uiData = UiData.EMPTY;
+
+ private tags: string[] = [];
+ private files: string[] = [];
+ private levels: string[] = [];
+ private searchString = '';
+
+ constructor(traces: Traces, notifyUiDataCallback: (data: UiData) => void) {
+ this.trace = assertDefined(traces.getTrace(TraceType.PROTO_LOG));
+ this.notifyUiDataCallback = notifyUiDataCallback;
+ this.originalIndicesOfFilteredOutputMessages = [];
+ this.computeUiDataMessages();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.entry = TraceEntryFinder.findCorrespondingEntry(this.trace, position);
+ this.computeUiDataCurrentMessageIndex();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onLogLevelsFilterChanged(levels: string[]) {
+ this.levels = levels;
+ this.computeUiDataMessages();
+ this.computeUiDataCurrentMessageIndex();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onTagsFilterChanged(tags: string[]) {
+ this.tags = tags;
+ this.computeUiDataMessages();
+ this.computeUiDataCurrentMessageIndex();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onSourceFilesFilterChanged(files: string[]) {
+ this.files = files;
+ this.computeUiDataMessages();
+ this.computeUiDataCurrentMessageIndex();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onSearchStringFilterChanged(searchString: string) {
+ this.searchString = searchString;
+ this.computeUiDataMessages();
+ this.computeUiDataCurrentMessageIndex();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ private computeUiDataMessages() {
+ const allLogLevels = this.getUniqueMessageValues((message: LogMessage) => message.level);
+ const allTags = this.getUniqueMessageValues((message: LogMessage) => message.tag);
+ const allSourceFiles = this.getUniqueMessageValues((message: LogMessage) => message.at);
+
+ let filteredMessagesAndOriginalIndex = new Array<[number, LogMessage]>();
+ this.trace.forEachEntry((entry) => {
+ filteredMessagesAndOriginalIndex.push([entry.getIndex(), entry.getValue()]);
+ });
+
+ if (this.levels.length > 0) {
+ filteredMessagesAndOriginalIndex = filteredMessagesAndOriginalIndex.filter((value) =>
+ this.levels.includes(value[1].level)
+ );
+ }
+
+ if (this.tags.length > 0) {
+ filteredMessagesAndOriginalIndex = filteredMessagesAndOriginalIndex.filter((value) =>
+ this.tags.includes(value[1].tag)
+ );
+ }
+
+ if (this.files.length > 0) {
+ filteredMessagesAndOriginalIndex = filteredMessagesAndOriginalIndex.filter((value) =>
+ this.files.includes(value[1].at)
+ );
+ }
+
+ filteredMessagesAndOriginalIndex = filteredMessagesAndOriginalIndex.filter((value) =>
+ value[1].text.includes(this.searchString)
+ );
+
+ this.originalIndicesOfFilteredOutputMessages = filteredMessagesAndOriginalIndex.map(
+ (value) => value[0]
+ );
+ const filteredMessages = filteredMessagesAndOriginalIndex.map((value) => value[1]);
+
+ this.uiData = new UiData(allLogLevels, allTags, allSourceFiles, filteredMessages, undefined);
+ }
+
+ private computeUiDataCurrentMessageIndex() {
+ if (!this.entry) {
+ this.uiData.currentMessageIndex = undefined;
+ return;
+ }
+
+ if (this.originalIndicesOfFilteredOutputMessages.length === 0) {
+ this.uiData.currentMessageIndex = undefined;
+ return;
+ }
+
+ this.uiData.currentMessageIndex =
+ ArrayUtils.binarySearchFirstGreaterOrEqual(
+ this.originalIndicesOfFilteredOutputMessages,
+ this.entry.getIndex()
+ ) ?? this.originalIndicesOfFilteredOutputMessages.length - 1;
+ }
+
+ private getUniqueMessageValues(getValue: (message: LogMessage) => string): string[] {
+ const uniqueValues = new Set<string>();
+ this.trace.forEachEntry((entry) => {
+ uniqueValues.add(getValue(entry.getValue()));
+ });
+ const result = [...uniqueValues];
+ result.sort();
+ return result;
+ }
+}
diff --git a/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts b/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts
new file mode 100644
index 0000000..cf461d8
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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 ANYf KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TracesBuilder} from 'test/unit/traces_builder';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {LogMessage} from 'trace/protolog';
+import {RealTimestamp} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+describe('ViewerProtoLogPresenter', () => {
+ let presenter: Presenter;
+ let inputMessages: LogMessage[];
+ let trace: Trace<LogMessage>;
+ let position10: TracePosition;
+ let position11: TracePosition;
+ let position12: TracePosition;
+ let outputUiData: undefined | UiData;
+
+ beforeEach(async () => {
+ const time10 = new RealTimestamp(10n);
+ const time11 = new RealTimestamp(11n);
+ const time12 = new RealTimestamp(12n);
+
+ inputMessages = [
+ new LogMessage('text0', 'time', 'tag0', 'level0', 'sourcefile0', 10n),
+ new LogMessage('text1', 'time', 'tag1', 'level1', 'sourcefile1', 11n),
+ new LogMessage('text2', 'time', 'tag2', 'level2', 'sourcefile2', 12n),
+ ];
+ trace = new TraceBuilder<LogMessage>()
+ .setEntries(inputMessages)
+ .setTimestamps([time10, time11, time12])
+ .build();
+
+ position10 = TracePosition.fromTimestamp(time10);
+ position11 = TracePosition.fromTimestamp(time11);
+ position12 = TracePosition.fromTimestamp(time12);
+
+ outputUiData = undefined;
+
+ const traces = new Traces();
+ traces.setTrace(TraceType.PROTO_LOG, trace);
+ presenter = new Presenter(traces, (data: UiData) => {
+ outputUiData = data;
+ });
+ });
+
+ it('is robust to empty trace', () => {
+ const traces = new TracesBuilder().setEntries(TraceType.PROTO_LOG, []).build();
+ presenter = new Presenter(traces, (data: UiData) => {
+ outputUiData = data;
+ });
+
+ expect(outputUiData!.messages).toEqual([]);
+ expect(outputUiData!.currentMessageIndex).toBeUndefined();
+
+ presenter.onTracePositionUpdate(position10);
+ expect(outputUiData!.messages).toEqual([]);
+ expect(outputUiData!.currentMessageIndex).toBeUndefined();
+ });
+
+ it('processes trace position updates', () => {
+ presenter.onTracePositionUpdate(position10);
+
+ expect(outputUiData!.allLogLevels).toEqual(['level0', 'level1', 'level2']);
+ expect(outputUiData!.allTags).toEqual(['tag0', 'tag1', 'tag2']);
+ expect(outputUiData!.allSourceFiles).toEqual(['sourcefile0', 'sourcefile1', 'sourcefile2']);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+ expect(outputUiData!.currentMessageIndex).toEqual(0);
+ });
+
+ it('updates displayed messages according to log levels filter', () => {
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onLogLevelsFilterChanged([]);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onLogLevelsFilterChanged(['level1']);
+ expect(outputUiData!.messages).toEqual([inputMessages[1]]);
+
+ presenter.onLogLevelsFilterChanged(['level0', 'level1', 'level2']);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+ });
+
+ it('updates displayed messages according to tags filter', () => {
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onTagsFilterChanged([]);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onTagsFilterChanged(['tag1']);
+ expect(outputUiData!.messages).toEqual([inputMessages[1]]);
+
+ presenter.onTagsFilterChanged(['tag0', 'tag1', 'tag2']);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+ });
+
+ it('updates displayed messages according to source files filter', () => {
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onSourceFilesFilterChanged([]);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onSourceFilesFilterChanged(['sourcefile1']);
+ expect(outputUiData!.messages).toEqual([inputMessages[1]]);
+
+ presenter.onSourceFilesFilterChanged(['sourcefile0', 'sourcefile1', 'sourcefile2']);
+ expect(outputUiData!.messages).toEqual(inputMessages);
+ });
+
+ it('updates displayed messages according to search string filter', () => {
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onSearchStringFilterChanged('');
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onSearchStringFilterChanged('text');
+ expect(outputUiData!.messages).toEqual(inputMessages);
+
+ presenter.onSearchStringFilterChanged('text0');
+ expect(outputUiData!.messages).toEqual([inputMessages[0]]);
+
+ presenter.onSearchStringFilterChanged('text1');
+ expect(outputUiData!.messages).toEqual([inputMessages[1]]);
+ });
+
+ it('computes current message index', () => {
+ // Position -> entry #0
+ presenter.onTracePositionUpdate(position10);
+ presenter.onLogLevelsFilterChanged([]);
+ expect(outputUiData!.currentMessageIndex).toEqual(0);
+
+ presenter.onLogLevelsFilterChanged(['level0']);
+ expect(outputUiData!.currentMessageIndex).toEqual(0);
+
+ presenter.onLogLevelsFilterChanged([]);
+ expect(outputUiData!.currentMessageIndex).toEqual(0);
+
+ // Position -> entry #1
+ presenter.onTracePositionUpdate(position11);
+ presenter.onLogLevelsFilterChanged([]);
+ expect(outputUiData!.currentMessageIndex).toEqual(1);
+
+ presenter.onLogLevelsFilterChanged(['level0']);
+ expect(outputUiData!.currentMessageIndex).toEqual(0);
+
+ presenter.onLogLevelsFilterChanged(['level1']);
+ expect(outputUiData!.currentMessageIndex).toEqual(0);
+
+ presenter.onLogLevelsFilterChanged(['level0', 'level1']);
+ expect(outputUiData!.currentMessageIndex).toEqual(1);
+
+ // Position -> entry #2
+ presenter.onTracePositionUpdate(position12);
+ presenter.onLogLevelsFilterChanged([]);
+ expect(outputUiData!.currentMessageIndex).toEqual(2);
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_protolog/ui_data.ts b/tools/winscope/src/viewers/viewer_protolog/ui_data.ts
new file mode 100644
index 0000000..5b1bfa6
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/ui_data.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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 {LogMessage} from 'trace/protolog';
+
+class UiData {
+ constructor(
+ public allLogLevels: string[],
+ public allTags: string[],
+ public allSourceFiles: string[],
+ public messages: LogMessage[],
+ public currentMessageIndex: undefined | number
+ ) {}
+
+ static EMPTY = new UiData([], [], [], [], undefined);
+}
+
+export {UiData};
diff --git a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts
new file mode 100644
index 0000000..f03f46a
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+import {Events} from './events';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+class ViewerProtoLog implements Viewer {
+ constructor(traces: Traces) {
+ this.htmlElement = document.createElement('viewer-protolog');
+
+ this.presenter = new Presenter(traces, (data: UiData) => {
+ (this.htmlElement as any).inputData = data;
+ });
+
+ this.htmlElement.addEventListener(Events.LogLevelsFilterChanged, (event) => {
+ return this.presenter.onLogLevelsFilterChanged((event as CustomEvent).detail);
+ });
+ this.htmlElement.addEventListener(Events.TagsFilterChanged, (event) => {
+ return this.presenter.onTagsFilterChanged((event as CustomEvent).detail);
+ });
+ this.htmlElement.addEventListener(Events.SourceFilesFilterChanged, (event) => {
+ return this.presenter.onSourceFilesFilterChanged((event as CustomEvent).detail);
+ });
+ this.htmlElement.addEventListener(Events.SearchStringFilterChanged, (event) => {
+ return this.presenter.onSearchStringFilterChanged((event as CustomEvent).detail);
+ });
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.presenter.onTracePositionUpdate(position);
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'ProtoLog',
+ TraceType.PROTO_LOG
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return ViewerProtoLog.DEPENDENCIES;
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.PROTO_LOG];
+ private htmlElement: HTMLElement;
+ private presenter: Presenter;
+}
+
+export {ViewerProtoLog};
diff --git a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component.ts b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component.ts
new file mode 100644
index 0000000..7a05d5f
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component.ts
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 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 {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
+import {Component, ElementRef, Inject, Input, ViewChild} from '@angular/core';
+import {MatSelectChange} from '@angular/material/select';
+import {Events} from './events';
+import {UiData} from './ui_data';
+
+@Component({
+ selector: 'viewer-protolog',
+ template: `
+ <div class="card-grid container">
+ <div class="filters">
+ <div class="log-level">
+ <mat-form-field appearance="fill">
+ <mat-label>Log level</mat-label>
+ <mat-select (selectionChange)="onLogLevelsChange($event)" multiple>
+ <mat-option *ngFor="let level of uiData.allLogLevels" [value]="level">
+ {{ level }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="tag">
+ <mat-form-field appearance="fill">
+ <mat-label>Tags</mat-label>
+ <mat-select (selectionChange)="onTagsChange($event)" multiple>
+ <mat-option *ngFor="let tag of uiData.allTags" [value]="tag">
+ {{ tag }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="source-file">
+ <mat-form-field appearance="fill">
+ <mat-label>Source files</mat-label>
+ <mat-select (selectionChange)="onSourceFilesChange($event)" multiple>
+ <mat-option *ngFor="let file of uiData.allSourceFiles" [value]="file">
+ {{ file }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="text">
+ <mat-form-field appearance="fill">
+ <mat-label>Search text</mat-label>
+ <input matInput [(ngModel)]="searchString" (input)="onSearchStringChange()" />
+ </mat-form-field>
+ </div>
+ </div>
+ <cdk-virtual-scroll-viewport itemSize="16" class="scroll-messages">
+ <div
+ *cdkVirtualFor="let message of uiData.messages; let i = index"
+ class="message"
+ [class.current-message]="isCurrentMessage(i)">
+ <div class="time">
+ <span class="mat-body-1">{{ message.time }}</span>
+ </div>
+ <div class="log-level">
+ <span class="mat-body-1">{{ message.level }}</span>
+ </div>
+ <div class="tag">
+ <span class="mat-body-1">{{ message.tag }}</span>
+ </div>
+ <div class="source-file">
+ <span class="mat-body-1">{{ message.at }}</span>
+ </div>
+ <div class="text">
+ <span class="mat-body-1">{{ message.text }}</span>
+ </div>
+ </div>
+ </cdk-virtual-scroll-viewport>
+ </div>
+ `,
+ styles: [
+ `
+ .container {
+ padding: 16px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .filters {
+ display: flex;
+ flex-direction: row;
+ margin-top: 16px;
+ }
+
+ .scroll-messages {
+ height: 100%;
+ flex: 1;
+ }
+
+ .message {
+ display: flex;
+ flex-direction: row;
+ overflow-wrap: anywhere;
+ }
+
+ .message.current-message {
+ background-color: #365179;
+ color: white;
+ }
+
+ .time {
+ flex: 2;
+ }
+
+ .log-level {
+ flex: 1;
+ }
+
+ .filters .log-level {
+ flex: 3;
+ }
+
+ .tag {
+ flex: 2;
+ }
+
+ .source-file {
+ flex: 4;
+ }
+
+ .text {
+ flex: 10;
+ }
+
+ .filters div {
+ margin: 4px;
+ }
+
+ .message div {
+ margin: 4px;
+ }
+
+ mat-form-field {
+ width: 100%;
+ }
+ `,
+ ],
+})
+export class ViewerProtologComponent {
+ constructor(@Inject(ElementRef) elementRef: ElementRef) {
+ this.elementRef = elementRef;
+ }
+
+ @Input()
+ set inputData(data: UiData) {
+ this.uiData = data;
+ if (this.uiData.currentMessageIndex !== undefined && this.scrollComponent) {
+ this.scrollComponent.scrollToIndex(this.uiData.currentMessageIndex);
+ }
+ }
+
+ onLogLevelsChange(event: MatSelectChange) {
+ this.emitEvent(Events.LogLevelsFilterChanged, event.value);
+ }
+
+ onTagsChange(event: MatSelectChange) {
+ this.emitEvent(Events.TagsFilterChanged, event.value);
+ }
+
+ onSourceFilesChange(event: MatSelectChange) {
+ this.emitEvent(Events.SourceFilesFilterChanged, event.value);
+ }
+
+ onSearchStringChange() {
+ this.emitEvent(Events.SearchStringFilterChanged, this.searchString);
+ }
+
+ isCurrentMessage(index: number): boolean {
+ return index === this.uiData.currentMessageIndex;
+ }
+
+ private emitEvent(event: string, data: any) {
+ const customEvent = new CustomEvent(event, {
+ bubbles: true,
+ detail: data,
+ });
+ this.elementRef.nativeElement.dispatchEvent(customEvent);
+ }
+
+ @ViewChild(CdkVirtualScrollViewport) scrollComponent!: CdkVirtualScrollViewport;
+
+ uiData: UiData = UiData.EMPTY;
+ private searchString = '';
+ private elementRef: ElementRef;
+}
diff --git a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts
new file mode 100644
index 0000000..493ef84
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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 {ScrollingModule} from '@angular/cdk/scrolling';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {ViewerProtologComponent} from './viewer_protolog_component';
+
+import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+
+describe('ViewerProtologComponent', () => {
+ let fixture: ComponentFixture<ViewerProtologComponent>;
+ let component: ViewerProtologComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [ScrollingModule],
+ declarations: [ViewerProtologComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ViewerProtologComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('creates message filters', () => {
+ expect(htmlElement.querySelector('.filters .log-level')).toBeTruthy();
+ expect(htmlElement.querySelector('.filters .tag')).toBeTruthy();
+ expect(htmlElement.querySelector('.filters .source-file')).toBeTruthy();
+ expect(htmlElement.querySelector('.filters .text')).toBeTruthy();
+ });
+
+ it('renders log messages', () => {
+ expect(htmlElement.querySelector('.scroll-messages')).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts
new file mode 100644
index 0000000..5bc1952
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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 {assertDefined} from 'common/assert_utils';
+import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+import {ViewerScreenRecordingComponent} from './viewer_screen_recording_component';
+
+class ViewerScreenRecording implements Viewer {
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.SCREEN_RECORDING];
+ private readonly trace: Trace<ScreenRecordingTraceEntry>;
+ private readonly htmlElement: HTMLElement;
+
+ constructor(traces: Traces) {
+ this.trace = assertDefined(traces.getTrace(TraceType.SCREEN_RECORDING));
+ this.htmlElement = document.createElement('viewer-screen-recording');
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ const entry = TraceEntryFinder.findCorrespondingEntry(this.trace, position);
+ (this.htmlElement as unknown as ViewerScreenRecordingComponent).currentTraceEntry =
+ entry?.getValue();
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.OVERLAY,
+ this.getDependencies(),
+ this.htmlElement,
+ 'ScreenRecording',
+ TraceType.SCREEN_RECORDING
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return ViewerScreenRecording.DEPENDENCIES;
+ }
+}
+
+export {ViewerScreenRecording};
diff --git a/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component.ts b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component.ts
new file mode 100644
index 0000000..b750dd1
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component.ts
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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 {Component, ElementRef, Inject, Input} from '@angular/core';
+import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
+import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+
+@Component({
+ selector: 'viewer-screen-recording',
+ template: `
+ <mat-card class="container">
+ <mat-card-title class="header">
+ <button mat-button class="button-drag" cdkDragHandle>
+ <mat-icon class="drag-icon">drag_indicator</mat-icon>
+ <span class="mat-body-2">Screen recording</span>
+ </button>
+
+ <button mat-button class="button-minimize" (click)="onMinimizeButtonClick()">
+ <mat-icon>
+ {{ isMinimized ? 'maximize' : 'minimize' }}
+ </mat-icon>
+ </button>
+ </mat-card-title>
+ <div class="video-container" [style.height]="isMinimized ? '0px' : ''">
+ <ng-container *ngIf="hasFrameToShow; then video; else noVideo"> </ng-container>
+ </div>
+ </mat-card>
+
+ <ng-template #video>
+ <video
+ *ngIf="hasFrameToShow"
+ [currentTime]="videoCurrentTime"
+ [src]="videoUrl"
+ cdkDragHandle></video>
+ </ng-template>
+
+ <ng-template #noVideo>
+ <div class="no-video">
+ <p class="mat-body-2">No screen recording frame to show.</p>
+ <p class="mat-body-1">Current timestamp is still before first frame.</p>
+ </div>
+ </ng-template>
+ `,
+ styles: [
+ `
+ .container {
+ width: fit-content;
+ height: fit-content;
+ display: flex;
+ flex-direction: column;
+ padding: 0;
+ }
+
+ .header {
+ display: flex;
+ flex-direction: row;
+ margin: 0px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ }
+
+ .button-drag {
+ flex-grow: 1;
+ cursor: grab;
+ }
+
+ .drag-icon {
+ float: left;
+ margin: 5px 0;
+ }
+
+ .button-minimize {
+ flex-grow: 0;
+ }
+
+ .video-container,
+ video {
+ border: 1px solid var(--default-border);
+ max-width: max(250px, 15vw);
+ cursor: grab;
+ overflow: hidden;
+ }
+
+ .no-video {
+ padding: 1rem;
+ text-align: center;
+ }
+ `,
+ ],
+})
+class ViewerScreenRecordingComponent {
+ constructor(
+ @Inject(ElementRef) elementRef: ElementRef,
+ @Inject(DomSanitizer) sanitizer: DomSanitizer
+ ) {
+ this.elementRef = elementRef;
+ this.sanitizer = sanitizer;
+ }
+
+ @Input()
+ set currentTraceEntry(entry: undefined | ScreenRecordingTraceEntry) {
+ if (entry === undefined) {
+ this.videoCurrentTime = undefined;
+ return;
+ }
+
+ if (this.videoUrl === undefined) {
+ this.videoUrl = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(entry.videoData));
+ }
+
+ this.videoCurrentTime = entry.videoTimeSeconds;
+ }
+
+ onMinimizeButtonClick() {
+ this.isMinimized = !this.isMinimized;
+ }
+
+ videoUrl: undefined | SafeUrl = undefined;
+ videoCurrentTime: number | undefined = undefined;
+ isMinimized = false;
+
+ private elementRef: ElementRef;
+ private sanitizer: DomSanitizer;
+
+ get hasFrameToShow() {
+ return this.videoCurrentTime !== undefined;
+ }
+}
+
+export {ViewerScreenRecordingComponent};
diff --git a/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component_test.ts b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component_test.ts
new file mode 100644
index 0000000..79baf12
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component_test.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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 {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatCardModule} from '@angular/material/card';
+import {ViewerScreenRecordingComponent} from './viewer_screen_recording_component';
+
+describe('ViewerScreenRecordingComponent', () => {
+ let fixture: ComponentFixture<ViewerScreenRecordingComponent>;
+ let component: ViewerScreenRecordingComponent;
+ let htmlElement: HTMLElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [MatCardModule],
+ declarations: [ViewerScreenRecordingComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(ViewerScreenRecordingComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('can be minimized and maximized', () => {
+ const buttonMinimize = htmlElement.querySelector('.button-minimize');
+ const videoContainer = htmlElement.querySelector('.video-container') as HTMLElement;
+ expect(buttonMinimize).toBeTruthy();
+ expect(videoContainer).toBeTruthy();
+ expect(videoContainer!.style.height).toEqual('');
+
+ buttonMinimize!.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ expect(videoContainer!.style.height).toEqual('0px');
+
+ buttonMinimize!.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ expect(videoContainer!.style.height).toEqual('');
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_stub.ts b/tools/winscope/src/viewers/viewer_stub.ts
new file mode 100644
index 0000000..46a7f99
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_stub.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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 {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {View, Viewer, ViewType} from './viewer';
+
+class ViewerStub implements Viewer {
+ constructor(title: string, viewContent?: string) {
+ this.title = title;
+
+ if (viewContent !== undefined) {
+ this.htmlElement = document.createElement('div');
+ this.htmlElement.innerText = viewContent;
+ } else {
+ this.htmlElement = undefined as unknown as HTMLElement;
+ }
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ // do nothing
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ this.title,
+ this.getDependencies()[0]
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return [TraceType.WINDOW_MANAGER];
+ }
+
+ private htmlElement: HTMLElement;
+ private title: string;
+}
+
+export {ViewerStub};
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts b/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts
new file mode 100644
index 0000000..57edcdb
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2022 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 {assertDefined} from 'common/assert_utils';
+import {PersistentStoreProxy} from 'common/persistent_store_proxy';
+import {FilterType, TreeUtils} from 'common/tree_utils';
+import {Layer} from 'trace/flickerlib/layers/Layer';
+import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {Rectangle, RectMatrix, RectTransform} from 'viewers/common/rectangle';
+import {TreeGenerator} from 'viewers/common/tree_generator';
+import {TreeTransformer} from 'viewers/common/tree_transformer';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {UiData} from './ui_data';
+
+type NotifyViewCallbackType = (uiData: UiData) => void;
+
+export class Presenter {
+ private readonly notifyViewCallback: NotifyViewCallbackType;
+ private readonly trace: Trace<LayerTraceEntry>;
+ private uiData: UiData;
+ private hierarchyFilter: FilterType = TreeUtils.makeNodeFilter('');
+ private propertiesFilter: FilterType = TreeUtils.makeNodeFilter('');
+ private highlightedItems: string[] = [];
+ private displayIds: number[] = [];
+ private pinnedItems: HierarchyTreeNode[] = [];
+ private pinnedIds: string[] = [];
+ private selectedHierarchyTree: HierarchyTreeNode | null = null;
+ private selectedLayer: LayerTraceEntry | Layer | null = null;
+ private previousEntry: LayerTraceEntry | null = null;
+ private entry: LayerTraceEntry | null = null;
+ private hierarchyUserOptions: UserOptions = PersistentStoreProxy.new<UserOptions>(
+ 'SfHierarchyOptions',
+ {
+ showDiff: {
+ name: 'Show diff', // TODO: PersistentStoreObject.Ignored("Show diff") or something like that to instruct to not store this info
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: false,
+ },
+ },
+ this.storage
+ );
+
+ private propertiesUserOptions: UserOptions = PersistentStoreProxy.new<UserOptions>(
+ 'SfPropertyOptions',
+ {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ showDefaults: {
+ name: 'Show defaults',
+ enabled: false,
+ tooltip: `
+ If checked, shows the value of all properties.
+ Otherwise, hides all properties whose value is
+ the default for its data type.
+ `,
+ },
+ },
+ this.storage
+ );
+
+ constructor(
+ traces: Traces,
+ private readonly storage: Storage,
+ notifyViewCallback: NotifyViewCallbackType
+ ) {
+ this.trace = assertDefined(traces.getTrace(TraceType.SURFACE_FLINGER));
+ this.notifyViewCallback = notifyViewCallback;
+ this.uiData = new UiData([TraceType.SURFACE_FLINGER]);
+ this.copyUiDataAndNotifyView();
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.uiData = new UiData();
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+
+ const entry = TraceEntryFinder.findCorrespondingEntry(this.trace, position);
+ const prevEntry =
+ entry && entry.getIndex() > 0 ? this.trace.getEntry(entry.getIndex() - 1) : undefined;
+
+ this.entry = entry?.getValue() ?? null;
+ this.previousEntry = prevEntry?.getValue() ?? null;
+ if (this.entry) {
+ this.uiData.highlightedItems = this.highlightedItems;
+ this.uiData.rects = this.generateRects();
+ this.uiData.displayIds = this.displayIds;
+ this.uiData.tree = this.generateTree();
+ }
+
+ this.copyUiDataAndNotifyView();
+ }
+
+ updatePinnedItems(pinnedItem: HierarchyTreeNode) {
+ const pinnedId = `${pinnedItem.id}`;
+ if (this.pinnedItems.map((item) => `${item.id}`).includes(pinnedId)) {
+ this.pinnedItems = this.pinnedItems.filter((pinned) => `${pinned.id}` !== pinnedId);
+ } else {
+ this.pinnedItems.push(pinnedItem);
+ }
+ this.updatePinnedIds(pinnedId);
+ this.uiData.pinnedItems = this.pinnedItems;
+ this.copyUiDataAndNotifyView();
+ }
+
+ updateHighlightedItems(id: string) {
+ if (this.highlightedItems.includes(id)) {
+ this.highlightedItems = this.highlightedItems.filter((hl) => hl !== id);
+ } else {
+ this.highlightedItems = []; //if multi-select surfaces implemented, remove this line
+ this.highlightedItems.push(id);
+ }
+ this.uiData.highlightedItems = this.highlightedItems;
+ this.copyUiDataAndNotifyView();
+ }
+
+ updateHierarchyTree(userOptions: UserOptions) {
+ this.hierarchyUserOptions = userOptions;
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.tree = this.generateTree();
+ this.copyUiDataAndNotifyView();
+ }
+
+ filterHierarchyTree(filterString: string) {
+ this.hierarchyFilter = TreeUtils.makeNodeFilter(filterString);
+ this.uiData.tree = this.generateTree();
+ this.copyUiDataAndNotifyView();
+ }
+
+ updatePropertiesTree(userOptions: UserOptions) {
+ this.propertiesUserOptions = userOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+ this.updateSelectedTreeUiData();
+ }
+
+ filterPropertiesTree(filterString: string) {
+ this.propertiesFilter = TreeUtils.makeNodeFilter(filterString);
+ this.updateSelectedTreeUiData();
+ }
+
+ newPropertiesTree(selectedItem: HierarchyTreeNode) {
+ this.selectedHierarchyTree = selectedItem;
+ this.updateSelectedTreeUiData();
+ }
+
+ private generateRects(): Rectangle[] {
+ const displayRects =
+ this.entry.displays.map((display: any) => {
+ const rect = display.layerStackSpace;
+ rect.label = 'Display';
+ if (display.name) {
+ rect.label += ` - ${display.name}`;
+ }
+ rect.stableId = `Display - ${display.id}`;
+ rect.displayId = display.layerStackId;
+ rect.isDisplay = true;
+ rect.cornerRadius = 0;
+ rect.isVirtual = display.isVirtual ?? false;
+ rect.transform = {
+ matrix: display.transform.matrix,
+ };
+ return rect;
+ }) ?? [];
+ this.displayIds = this.entry.displays.map((it: any) => it.layerStackId);
+ this.displayIds.sort();
+ const rects = this.getLayersForRectsView()
+ .sort(this.compareLayerZ)
+ .map((it: any) => {
+ const rect = it.rect;
+ rect.displayId = it.stackId;
+ rect.cornerRadius = it.cornerRadius;
+ if (!this.displayIds.includes(it.stackId)) {
+ this.displayIds.push(it.stackId);
+ }
+ rect.transform = {
+ matrix: rect.transform.matrix,
+ };
+ return rect;
+ });
+
+ return this.rectsToUiData(rects.concat(displayRects));
+ }
+
+ private getLayersForRectsView(): Layer[] {
+ const onlyVisible = this.hierarchyUserOptions['onlyVisible']?.enabled ?? false;
+ // Show only visible layers or Visible + Occluded layers. Don't show all layers
+ // (flattenedLayers) because container layers are never meant to be displayed
+ return this.entry.flattenedLayers.filter(
+ (it: any) => it.isVisible || (!onlyVisible && it.occludedBy.length > 0)
+ );
+ }
+
+ private compareLayerZ(a: Layer, b: Layer): number {
+ const zipLength = Math.min(a.zOrderPath.length, b.zOrderPath.length);
+ for (let i = 0; i < zipLength; ++i) {
+ const zOrderA = a.zOrderPath[i];
+ const zOrderB = b.zOrderPath[i];
+ if (zOrderA > zOrderB) return -1;
+ if (zOrderA < zOrderB) return 1;
+ }
+ return b.zOrderPath.length - a.zOrderPath.length;
+ }
+
+ private updateSelectedTreeUiData() {
+ if (this.selectedHierarchyTree) {
+ this.uiData.propertiesTree = this.getTreeWithTransformedProperties(
+ this.selectedHierarchyTree
+ );
+ this.uiData.selectedLayer = this.selectedLayer;
+ this.uiData.displayPropertyGroups = this.shouldDisplayPropertyGroups(this.selectedLayer);
+ }
+ this.copyUiDataAndNotifyView();
+ }
+
+ private generateTree() {
+ if (!this.entry) {
+ return null;
+ }
+
+ const generator = new TreeGenerator(this.entry, this.hierarchyFilter, this.pinnedIds)
+ .setIsOnlyVisibleView(this.hierarchyUserOptions['onlyVisible']?.enabled)
+ .setIsSimplifyNames(this.hierarchyUserOptions['simplifyNames']?.enabled)
+ .setIsFlatView(this.hierarchyUserOptions['flat']?.enabled)
+ .withUniqueNodeId();
+ let tree: HierarchyTreeNode | null;
+ if (!this.hierarchyUserOptions['showDiff']?.enabled) {
+ tree = generator.generateTree();
+ } else {
+ tree = generator
+ .compareWith(this.previousEntry)
+ .withModifiedCheck()
+ .generateFinalTreeWithDiff();
+ }
+ this.pinnedItems = generator.getPinnedItems();
+ this.uiData.pinnedItems = this.pinnedItems;
+ return tree;
+ }
+
+ private rectsToUiData(rects: any[]): Rectangle[] {
+ const uiRects: Rectangle[] = [];
+ rects.forEach((rect: any) => {
+ let t = null;
+ if (rect.transform && rect.transform.matrix) {
+ t = rect.transform.matrix;
+ } else if (rect.transform) {
+ t = rect.transform;
+ }
+ let transform: RectTransform | null = null;
+ if (t !== null) {
+ const matrix: RectMatrix = {
+ dsdx: t.dsdx,
+ dsdy: t.dsdy,
+ dtdx: t.dtdx,
+ dtdy: t.dtdy,
+ tx: t.tx,
+ ty: t.ty,
+ };
+ transform = {
+ matrix,
+ };
+ }
+
+ const newRect: Rectangle = {
+ topLeft: {x: rect.left, y: rect.top},
+ bottomRight: {x: rect.right, y: rect.bottom},
+ label: rect.label,
+ transform,
+ isVisible: rect.ref?.isVisible ?? false,
+ isDisplay: rect.isDisplay ?? false,
+ ref: rect.ref,
+ id: rect.stableId ?? rect.ref.stableId,
+ displayId: rect.displayId ?? rect.ref.stackId,
+ isVirtual: rect.isVirtual ?? false,
+ isClickable: !(rect.isDisplay ?? false),
+ cornerRadius: rect.cornerRadius,
+ };
+ uiRects.push(newRect);
+ });
+ return uiRects;
+ }
+
+ private updatePinnedIds(newId: string) {
+ if (this.pinnedIds.includes(newId)) {
+ this.pinnedIds = this.pinnedIds.filter((pinned) => pinned !== newId);
+ } else {
+ this.pinnedIds.push(newId);
+ }
+ }
+
+ private getTreeWithTransformedProperties(selectedTree: HierarchyTreeNode): PropertiesTreeNode {
+ const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
+ .setOnlyProtoDump(true)
+ .setIsShowDefaults(this.propertiesUserOptions['showDefaults']?.enabled)
+ .setIsShowDiff(this.propertiesUserOptions['showDiff']?.enabled)
+ .setTransformerOptions({skip: selectedTree.skip})
+ .setProperties(this.entry)
+ .setDiffProperties(this.previousEntry);
+ this.selectedLayer = transformer.getOriginalFlickerItem(this.entry, selectedTree.stableId);
+ const transformedTree = transformer.transform();
+ return transformedTree;
+ }
+
+ private shouldDisplayPropertyGroups(selectedLayer: Layer): boolean {
+ // Do not display property groups when the root layer is selected. The root layer doesn't
+ // provide property groups info (visibility, geometry transforms, ...).
+ const isRoot = selectedLayer === this.entry;
+ return !isRoot;
+ }
+
+ private copyUiDataAndNotifyView() {
+ // Create a shallow copy of the data, otherwise the Angular OnPush change detection strategy
+ // won't detect the new input
+ const copy = Object.assign({}, this.uiData);
+ this.notifyViewCallback(copy);
+ }
+}
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts b/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts
new file mode 100644
index 0000000..484852b
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2022 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 {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {MockStorage} from 'test/unit/mock_storage';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {UnitTestUtils} from 'test/unit/utils';
+import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
+import {RealTimestamp} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+describe('PresenterSurfaceFlinger', () => {
+ let trace: Trace<LayerTraceEntry>;
+ let position: TracePosition;
+ let positionMultiDisplayEntry: TracePosition;
+ let presenter: Presenter;
+ let uiData: UiData;
+ let selectedTree: HierarchyTreeNode;
+
+ beforeAll(async () => {
+ trace = new TraceBuilder<LayerTraceEntry>()
+ .setEntries([
+ await UnitTestUtils.getLayerTraceEntry(),
+ await UnitTestUtils.getMultiDisplayLayerTraceEntry(),
+ ])
+ .build();
+
+ position = TracePosition.fromTraceEntry(trace.getEntry(0));
+ positionMultiDisplayEntry = TracePosition.fromTraceEntry(trace.getEntry(1));
+
+ selectedTree = new HierarchyTreeBuilder()
+ .setName('Dim layer#53')
+ .setStableId('EffectLayer 53 Dim layer#53')
+ .setFilteredView(true)
+ .setKind('53')
+ .setDiffType('EffectLayer')
+ .setId(53)
+ .build();
+ });
+
+ beforeEach(async () => {
+ presenter = createPresenter(trace);
+ });
+
+ it('is robust to empty trace', () => {
+ const emptyTrace = new TraceBuilder<LayerTraceEntry>().setEntries([]).build();
+ const presenter = createPresenter(emptyTrace);
+
+ const positionWithoutTraceEntry = TracePosition.fromTimestamp(new RealTimestamp(0n));
+ presenter.onTracePositionUpdate(positionWithoutTraceEntry);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.tree).toBeFalsy();
+ });
+
+ it('processes trace position updates', () => {
+ presenter.onTracePositionUpdate(position);
+
+ expect(uiData.rects.length).toBeGreaterThan(0);
+ expect(uiData.highlightedItems?.length).toEqual(0);
+ expect(uiData.displayIds).toContain(0);
+ const hierarchyOpts = uiData.hierarchyUserOptions
+ ? Object.keys(uiData.hierarchyUserOptions)
+ : null;
+ expect(hierarchyOpts).toBeTruthy();
+ const propertyOpts = uiData.propertiesUserOptions
+ ? Object.keys(uiData.propertiesUserOptions)
+ : null;
+ expect(propertyOpts).toBeTruthy();
+ expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
+ });
+
+ it('creates input data for rects view', () => {
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.rects.length).toBeGreaterThan(0);
+ expect(uiData.rects[0].topLeft).toEqual({x: 0, y: 0});
+ expect(uiData.rects[0].bottomRight).toEqual({x: 1080, y: 118});
+ });
+
+ it('updates pinned items', () => {
+ expect(uiData.pinnedItems).toEqual([]);
+
+ const pinnedItem = new HierarchyTreeBuilder()
+ .setName('FirstPinnedItem')
+ .setStableId('TestItem 4')
+ .setLayerId(4)
+ .build();
+ presenter.updatePinnedItems(pinnedItem);
+ expect(uiData.pinnedItems).toContain(pinnedItem);
+ });
+
+ it('updates highlighted items', () => {
+ expect(uiData.highlightedItems).toEqual([]);
+
+ const id = '4';
+ presenter.updateHighlightedItems(id);
+ expect(uiData.highlightedItems).toContain(id);
+ });
+
+ it('updates hierarchy tree', () => {
+ //change flat view to true
+ const userOptions: UserOptions = {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: true,
+ },
+ };
+
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.tree?.children.length).toEqual(3);
+
+ presenter.updateHierarchyTree(userOptions);
+ expect(uiData.hierarchyUserOptions).toEqual(userOptions);
+ // nested children should now be on same level as initial parents
+ expect(uiData.tree?.children.length).toEqual(94);
+ });
+
+ it('filters hierarchy tree', () => {
+ const userOptions: UserOptions = {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: true,
+ },
+ };
+ presenter.onTracePositionUpdate(position);
+ presenter.updateHierarchyTree(userOptions);
+ expect(uiData.tree?.children.length).toEqual(94);
+ presenter.filterHierarchyTree('Wallpaper');
+ // All but four layers should be filtered out
+ expect(uiData.tree?.children.length).toEqual(4);
+ });
+
+ it('sets properties tree and associated ui data', () => {
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ // does not check specific tree values as tree transformation method may change
+ expect(uiData.propertiesTree).toBeTruthy();
+ });
+
+ it('updates properties tree', () => {
+ //change flat view to true
+ const userOptions: UserOptions = {
+ showDiff: {
+ name: 'Show diff',
+ enabled: true,
+ },
+ showDefaults: {
+ name: 'Show defaults',
+ enabled: true,
+ tooltip: `
+ If checked, shows the value of all properties.
+ Otherwise, hides all properties whose value is
+ the default for its data type.
+ `,
+ },
+ };
+
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ expect(uiData.propertiesTree?.diffType).toBeFalsy();
+
+ presenter.updatePropertiesTree(userOptions);
+ expect(uiData.propertiesUserOptions).toEqual(userOptions);
+ expect(uiData.propertiesTree?.diffType).toBeTruthy();
+ });
+
+ it('filters properties tree', () => {
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ let nonTerminalChildren =
+ uiData.propertiesTree?.children?.filter(
+ (child: PropertiesTreeNode) => typeof child.propertyKey === 'string'
+ ) ?? [];
+
+ expect(nonTerminalChildren.length).toEqual(22);
+ presenter.filterPropertiesTree('bound');
+
+ nonTerminalChildren =
+ uiData.propertiesTree?.children?.filter(
+ (child: PropertiesTreeNode) => typeof child.propertyKey === 'string'
+ ) ?? [];
+ expect(nonTerminalChildren.length).toEqual(3);
+ });
+
+ it('handles displays with no visible layers', async () => {
+ presenter.onTracePositionUpdate(positionMultiDisplayEntry);
+ expect(uiData.displayIds.length).toEqual(5);
+ // we want the ids to be sorted
+ expect(uiData.displayIds).toEqual([0, 2, 3, 4, 5]);
+ });
+
+ const createPresenter = (trace: Trace<LayerTraceEntry>): Presenter => {
+ const traces = new Traces();
+ traces.setTrace(TraceType.SURFACE_FLINGER, trace);
+ return new Presenter(traces, new MockStorage(), (newData: UiData) => {
+ uiData = newData;
+ });
+ };
+});
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/ui_data.ts b/tools/winscope/src/viewers/viewer_surface_flinger/ui_data.ts
new file mode 100644
index 0000000..319bf96
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/ui_data.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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 {Layer} from 'trace/flickerlib/common';
+import {TraceType} from 'trace/trace_type';
+import {Rectangle} from 'viewers/common/rectangle';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+
+export class UiData {
+ dependencies: TraceType[];
+ rects: Rectangle[] = [];
+ displayIds: number[] = [];
+ highlightedItems: string[] = [];
+ pinnedItems: HierarchyTreeNode[] = [];
+ hierarchyUserOptions: UserOptions = {};
+ propertiesUserOptions: UserOptions = {};
+ tree: HierarchyTreeNode | null = null;
+ propertiesTree: PropertiesTreeNode | null = null;
+ selectedLayer: Layer = {};
+ displayPropertyGroups = true;
+
+ constructor(dependencies?: TraceType[]) {
+ this.dependencies = dependencies ?? [];
+ }
+}
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
new file mode 100644
index 0000000..17dc629
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+class ViewerSurfaceFlinger implements Viewer {
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.SURFACE_FLINGER];
+ private readonly htmlElement: HTMLElement;
+ private readonly presenter: Presenter;
+
+ constructor(traces: Traces, storage: Storage) {
+ this.htmlElement = document.createElement('viewer-surface-flinger');
+
+ this.presenter = new Presenter(traces, storage, (uiData: UiData) => {
+ (this.htmlElement as any).inputData = uiData;
+ });
+
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) =>
+ this.presenter.updatePinnedItems((event as CustomEvent).detail.pinnedItem)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HighlightedChange, (event) =>
+ this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) =>
+ this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyFilterChange, (event) =>
+ this.presenter.filterHierarchyTree((event as CustomEvent).detail.filterString)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.PropertiesUserOptionsChange, (event) =>
+ this.presenter.updatePropertiesTree((event as CustomEvent).detail.userOptions)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.PropertiesFilterChange, (event) =>
+ this.presenter.filterPropertiesTree((event as CustomEvent).detail.filterString)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.SelectedTreeChange, (event) =>
+ this.presenter.newPropertiesTree((event as CustomEvent).detail.selectedItem)
+ );
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.presenter.onTracePositionUpdate(position);
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Surface Flinger',
+ TraceType.SURFACE_FLINGER
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return ViewerSurfaceFlinger.DEPENDENCIES;
+ }
+}
+
+export {ViewerSurfaceFlinger};
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component.ts b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component.ts
new file mode 100644
index 0000000..5613126
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 {ChangeDetectionStrategy, Component, Input} from '@angular/core';
+import {TRACE_INFO} from 'app/trace_info';
+import {PersistentStore} from 'common/persistent_store';
+import {TraceType} from 'trace/trace_type';
+import {UiData} from './ui_data';
+
+@Component({
+ selector: 'viewer-surface-flinger',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+ <div class="card-grid">
+ <rects-view
+ class="rects-view"
+ title="Layers"
+ [rects]="inputData?.rects ?? []"
+ [highlightedItems]="inputData?.highlightedItems ?? []"
+ [displayIds]="inputData?.displayIds ?? []"></rects-view>
+ <mat-divider [vertical]="true"></mat-divider>
+ <hierarchy-view
+ class="hierarchy-view"
+ [tree]="inputData?.tree ?? null"
+ [dependencies]="inputData?.dependencies ?? []"
+ [highlightedItems]="inputData?.highlightedItems ?? []"
+ [pinnedItems]="inputData?.pinnedItems ?? []"
+ [store]="store"
+ [userOptions]="inputData?.hierarchyUserOptions ?? {}"></hierarchy-view>
+ <mat-divider [vertical]="true"></mat-divider>
+ <properties-view
+ class="properties-view"
+ [userOptions]="inputData?.propertiesUserOptions ?? {}"
+ [propertiesTree]="inputData?.propertiesTree ?? {}"
+ [selectedFlickerItem]="inputData?.selectedLayer ?? {}"
+ [displayPropertyGroups]="inputData?.displayPropertyGroups"
+ [isProtoDump]="true"></properties-view>
+ </div>
+ `,
+ styles: [
+ `
+ .rects-view,
+ .hierarchy-view,
+ .properties-view {
+ flex: 1;
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ }
+ `,
+ ],
+})
+export class ViewerSurfaceFlingerComponent {
+ @Input() inputData?: UiData;
+ @Input() store: PersistentStore = new PersistentStore();
+ @Input() active = false;
+ TRACE_INFO = TRACE_INFO;
+ TraceType = TraceType;
+}
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component_test.ts b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component_test.ts
new file mode 100644
index 0000000..c0b3135
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component_test.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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 {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatIconModule} from '@angular/material/icon';
+import {HierarchyComponent} from 'viewers/components/hierarchy_component';
+import {PropertiesComponent} from 'viewers/components/properties_component';
+import {RectsComponent} from 'viewers/components/rects/rects_component';
+import {ViewerSurfaceFlingerComponent} from './viewer_surface_flinger_component';
+
+describe('ViewerSurfaceFlingerComponent', () => {
+ let fixture: ComponentFixture<ViewerSurfaceFlingerComponent>;
+ let component: ViewerSurfaceFlingerComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [MatIconModule, MatDividerModule],
+ declarations: [
+ ViewerSurfaceFlingerComponent,
+ HierarchyComponent,
+ PropertiesComponent,
+ RectsComponent,
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ViewerSurfaceFlingerComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('creates rects view', () => {
+ const rectsView = htmlElement.querySelector('.rects-view');
+ expect(rectsView).toBeTruthy();
+ });
+
+ it('creates hierarchy view', () => {
+ const hierarchyView = htmlElement.querySelector('.hierarchy-view');
+ expect(hierarchyView).toBeTruthy();
+ });
+
+ it('creates properties view', () => {
+ const propertiesView = htmlElement.querySelector('.properties-view');
+ expect(propertiesView).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/src/viewers/viewer_transactions/events.ts b/tools/winscope/src/viewers/viewer_transactions/events.ts
new file mode 100644
index 0000000..913630e
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/events.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+class Events {
+ static VSyncIdFilterChanged = 'ViewerTransactionsEvent_VSyncIdFilterChanged';
+ static PidFilterChanged = 'ViewerTransactionsEvent_PidFilterChanged';
+ static UidFilterChanged = 'ViewerTransactionsEvent_UidFilterChanged';
+ static TypeFilterChanged = 'ViewerTransactionsEvent_TypeFilterChanged';
+ static LayerIdFilterChanged = 'ViewerTransactionsEvent_LayerIdFilterChanged';
+ static WhatSearchStringChanged = 'ViewerTransactionsEvent_WhatSearchStringChanged';
+ static EntryClicked = 'ViewerTransactionsEvent_EntryClicked';
+ static IdFilterChanges = 'ViewerTransactionsEvent_IdFilterChanged';
+}
+
+export {Events};
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter.ts b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
new file mode 100644
index 0000000..d4a0910
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2022 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 {ArrayUtils} from 'common/array_utils';
+import {assertDefined} from 'common/assert_utils';
+import {TimeUtils} from 'common/time_utils';
+import {ObjectFormatter} from 'trace/flickerlib/ObjectFormatter';
+import {Trace, TraceEntry} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {PropertiesTreeGenerator} from 'viewers/common/properties_tree_generator';
+import {PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UiData, UiDataEntry, UiDataEntryType} from './ui_data';
+
+export class Presenter {
+ private trace: Trace<object>;
+ private entry?: TraceEntry<object>;
+ private originalIndicesOfUiDataEntries: number[];
+ private uiData = UiData.EMPTY;
+ private readonly notifyUiDataCallback: (data: UiData) => void;
+ private static readonly VALUE_NA = 'N/A';
+ private vsyncIdFilter: string[] = [];
+ private pidFilter: string[] = [];
+ private uidFilter: string[] = [];
+ private typeFilter: string[] = [];
+ private layerIdFilter: string[] = [];
+ private idFilter: string | undefined = undefined;
+ private whatSearchString = '';
+
+ constructor(traces: Traces, notifyUiDataCallback: (data: UiData) => void) {
+ this.trace = assertDefined(traces.getTrace(TraceType.TRANSACTIONS));
+ this.notifyUiDataCallback = notifyUiDataCallback;
+ this.originalIndicesOfUiDataEntries = [];
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.entry = TraceEntryFinder.findCorrespondingEntry(this.trace, position);
+
+ this.uiData.currentEntryIndex = this.computeCurrentEntryIndex();
+ this.uiData.selectedEntryIndex = undefined;
+ this.uiData.scrollToIndex = this.uiData.currentEntryIndex;
+ this.uiData.currentPropertiesTree = this.computeCurrentPropertiesTree(
+ this.uiData.entries,
+ this.uiData.currentEntryIndex,
+ this.uiData.selectedEntryIndex
+ );
+
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onVSyncIdFilterChanged(vsyncIds: string[]) {
+ this.vsyncIdFilter = vsyncIds;
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onPidFilterChanged(pids: string[]) {
+ this.pidFilter = pids;
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onUidFilterChanged(uids: string[]) {
+ this.uidFilter = uids;
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onTypeFilterChanged(types: string[]) {
+ this.typeFilter = types;
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onLayerIdFilterChanged(ids: string[]) {
+ this.layerIdFilter = ids;
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onIdFilterChanged(id: string) {
+ if (id === '') {
+ this.idFilter = undefined;
+ } else {
+ this.idFilter = id;
+ }
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onWhatSearchStringChanged(searchString: string) {
+ this.whatSearchString = searchString;
+ this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onEntryClicked(index: number) {
+ if (this.uiData.selectedEntryIndex === index) {
+ this.uiData.selectedEntryIndex = undefined; // remove selection when clicked again
+ } else {
+ this.uiData.selectedEntryIndex = index;
+ }
+
+ this.uiData.scrollToIndex = undefined; // no scrolling
+
+ this.uiData.currentPropertiesTree = this.computeCurrentPropertiesTree(
+ this.uiData.entries,
+ this.uiData.currentEntryIndex,
+ this.uiData.selectedEntryIndex
+ );
+
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ private computeUiData() {
+ const entries = this.makeUiDataEntries();
+
+ const allVSyncIds = this.getUniqueUiDataEntryValues(entries, (entry: UiDataEntry) =>
+ entry.vsyncId.toString()
+ );
+ const allPids = this.getUniqueUiDataEntryValues(entries, (entry: UiDataEntry) => entry.pid);
+ const allUids = this.getUniqueUiDataEntryValues(entries, (entry: UiDataEntry) => entry.uid);
+ const allTypes = this.getUniqueUiDataEntryValues(entries, (entry: UiDataEntry) => entry.type);
+ const allLayerAndDisplayIds = this.getUniqueUiDataEntryValues(
+ entries,
+ (entry: UiDataEntry) => entry.layerOrDisplayId
+ );
+ const allTransactionIds = this.getUniqueUiDataEntryValues(
+ entries,
+ (entry: UiDataEntry) => entry.transactionId
+ );
+
+ let filteredEntries = entries;
+
+ if (this.vsyncIdFilter.length > 0) {
+ filteredEntries = filteredEntries.filter((entry) =>
+ this.vsyncIdFilter.includes(entry.vsyncId.toString())
+ );
+ }
+
+ if (this.pidFilter.length > 0) {
+ filteredEntries = filteredEntries.filter((entry) => this.pidFilter.includes(entry.pid));
+ }
+
+ if (this.uidFilter.length > 0) {
+ filteredEntries = filteredEntries.filter((entry) => this.uidFilter.includes(entry.uid));
+ }
+
+ if (this.typeFilter.length > 0) {
+ filteredEntries = filteredEntries.filter((entry) => this.typeFilter.includes(entry.type));
+ }
+
+ if (this.layerIdFilter.length > 0) {
+ filteredEntries = filteredEntries.filter((entry) =>
+ this.layerIdFilter.includes(entry.layerOrDisplayId)
+ );
+ }
+ if (this.idFilter !== undefined) {
+ filteredEntries = filteredEntries.filter(
+ (entry) => entry.transactionId.toString() === this.idFilter
+ );
+ }
+
+ filteredEntries = filteredEntries.filter((entry) => entry.what.includes(this.whatSearchString));
+
+ this.originalIndicesOfUiDataEntries = filteredEntries.map(
+ (entry) => entry.originalIndexInTraceEntry
+ );
+
+ const currentEntryIndex = this.computeCurrentEntryIndex();
+ const selectedEntryIndex = undefined;
+ const currentPropertiesTree = this.computeCurrentPropertiesTree(
+ filteredEntries,
+ currentEntryIndex,
+ selectedEntryIndex
+ );
+
+ this.uiData = new UiData(
+ allVSyncIds,
+ allPids,
+ allUids,
+ allTypes,
+ allLayerAndDisplayIds,
+ allTransactionIds,
+ filteredEntries,
+ currentEntryIndex,
+ selectedEntryIndex,
+ currentEntryIndex,
+ currentPropertiesTree
+ );
+ }
+
+ private computeCurrentEntryIndex(): undefined | number {
+ if (!this.entry) {
+ return undefined;
+ }
+
+ if (this.originalIndicesOfUiDataEntries.length === 0) {
+ return undefined;
+ }
+
+ return (
+ ArrayUtils.binarySearchFirstGreaterOrEqual(
+ this.originalIndicesOfUiDataEntries,
+ this.entry.getIndex()
+ ) ?? this.originalIndicesOfUiDataEntries.length - 1
+ );
+ }
+
+ private computeCurrentPropertiesTree(
+ entries: UiDataEntry[],
+ currentEntryIndex: undefined | number,
+ selectedEntryIndex: undefined | number
+ ): undefined | PropertiesTreeNode {
+ if (selectedEntryIndex !== undefined) {
+ return entries[selectedEntryIndex].propertiesTree;
+ }
+ if (currentEntryIndex !== undefined) {
+ return entries[currentEntryIndex].propertiesTree;
+ }
+ return undefined;
+ }
+
+ private makeUiDataEntries(): UiDataEntry[] {
+ const treeGenerator = new PropertiesTreeGenerator();
+ const entries: UiDataEntry[] = [];
+ const formattingOptions = ObjectFormatter.displayDefaults;
+ ObjectFormatter.displayDefaults = true;
+
+ this.trace.forEachEntry((entry, originalIndex) => {
+ const timestampType = entry.getTimestamp().getType();
+ const entryProto = entry.getValue() as any;
+ const realToElapsedTimeOffsetNs = entryProto.realToElapsedTimeOffsetNs;
+
+ for (const transactionStateProto of entryProto.transactions) {
+ for (const layerStateProto of transactionStateProto.layerChanges) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ transactionStateProto.pid.toString(),
+ transactionStateProto.uid.toString(),
+ UiDataEntryType.LAYER_CHANGED,
+ layerStateProto.layerId.toString(),
+ transactionStateProto.transactionId.toString(),
+ layerStateProto.what,
+ treeGenerator.generate('LayerState', ObjectFormatter.format(layerStateProto))
+ )
+ );
+ }
+
+ for (const displayStateProto of transactionStateProto.displayChanges) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ transactionStateProto.pid.toString(),
+ transactionStateProto.uid.toString(),
+ UiDataEntryType.DISPLAY_CHANGED,
+ displayStateProto.id.toString(),
+ transactionStateProto.transactionId.toString(),
+ displayStateProto.what,
+ treeGenerator.generate('DisplayState', ObjectFormatter.format(displayStateProto))
+ )
+ );
+ }
+
+ if (
+ transactionStateProto.layerChanges.length === 0 &&
+ transactionStateProto.displayChanges.length === 0
+ ) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ transactionStateProto.pid.toString(),
+ transactionStateProto.uid.toString(),
+ UiDataEntryType.NO_OP,
+ '',
+ transactionStateProto.transactionId.toString(),
+ '',
+ {}
+ )
+ );
+ }
+ }
+
+ for (const layerCreationArgsProto of entryProto.addedLayers) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ Presenter.VALUE_NA,
+ Presenter.VALUE_NA,
+ UiDataEntryType.LAYER_ADDED,
+ layerCreationArgsProto.layerId.toString(),
+ '',
+ '',
+ treeGenerator.generate(
+ 'LayerCreationArgs',
+ ObjectFormatter.format(layerCreationArgsProto)
+ )
+ )
+ );
+ }
+
+ for (const destroyedLayerId of entryProto.destroyedLayers) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ Presenter.VALUE_NA,
+ Presenter.VALUE_NA,
+ UiDataEntryType.LAYER_DESTROYED,
+ destroyedLayerId.toString(),
+ '',
+ '',
+ treeGenerator.generate('DestroyedLayerId', ObjectFormatter.format(destroyedLayerId))
+ )
+ );
+ }
+
+ for (const displayStateProto of entryProto.addedDisplays) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ Presenter.VALUE_NA,
+ Presenter.VALUE_NA,
+ UiDataEntryType.DISPLAY_ADDED,
+ displayStateProto.id.toString(),
+ '',
+ displayStateProto.what,
+ treeGenerator.generate('DisplayState', ObjectFormatter.format(displayStateProto))
+ )
+ );
+ }
+
+ for (const removedDisplayId of entryProto.removedDisplays) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ Presenter.VALUE_NA,
+ Presenter.VALUE_NA,
+ UiDataEntryType.DISPLAY_REMOVED,
+ removedDisplayId.toString(),
+ '',
+ '',
+ treeGenerator.generate('RemovedDisplayId', ObjectFormatter.format(removedDisplayId))
+ )
+ );
+ }
+
+ for (const destroyedLayerHandleId of entryProto.destroyedLayerHandles) {
+ entries.push(
+ new UiDataEntry(
+ originalIndex,
+ TimeUtils.format(entry.getTimestamp()),
+ Number(entryProto.vsyncId),
+ Presenter.VALUE_NA,
+ Presenter.VALUE_NA,
+ UiDataEntryType.LAYER_HANDLE_DESTROYED,
+ destroyedLayerHandleId.toString(),
+ '',
+ '',
+ treeGenerator.generate(
+ 'DestroyedLayerHandleId',
+ ObjectFormatter.format(destroyedLayerHandleId)
+ )
+ )
+ );
+ }
+ });
+
+ ObjectFormatter.displayDefaults = formattingOptions;
+
+ return entries;
+ }
+
+ private getUniqueUiDataEntryValues<T>(
+ entries: UiDataEntry[],
+ getValue: (entry: UiDataEntry) => T
+ ): T[] {
+ const uniqueValues = new Set<T>();
+ entries.forEach((entry: UiDataEntry) => {
+ uniqueValues.add(getValue(entry));
+ });
+
+ const result = [...uniqueValues];
+
+ result.sort((a, b) => {
+ const aIsNumber = !isNaN(Number(a));
+ const bIsNumber = !isNaN(Number(b));
+
+ if (aIsNumber && bIsNumber) {
+ return Number(a) - Number(b);
+ } else if (aIsNumber) {
+ return 1; // place number after strings in the result
+ } else if (bIsNumber) {
+ return -1; // place number after strings in the result
+ }
+
+ // a and b are both strings
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+
+ return result;
+ }
+}
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
new file mode 100644
index 0000000..7ea260e
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2022 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 ANYf KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TracesBuilder} from 'test/unit/traces_builder';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
+import {RealTimestamp, TimestampType} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {Presenter} from './presenter';
+import {UiData, UiDataEntryType} from './ui_data';
+
+describe('PresenterTransactions', () => {
+ let parser: Parser<object>;
+ let trace: Trace<object>;
+ let traces: Traces;
+ let presenter: Presenter;
+ let outputUiData: undefined | UiData;
+ const TOTAL_OUTPUT_ENTRIES = 1647;
+
+ beforeAll(async () => {
+ parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/Transactions.pb');
+ });
+
+ beforeEach(() => {
+ outputUiData = undefined;
+ setUpTestEnvironment(TimestampType.ELAPSED);
+ });
+
+ it('is robust to empty trace', () => {
+ const traces = new TracesBuilder().setEntries(TraceType.TRANSACTIONS, []).build();
+ presenter = new Presenter(traces, (data: UiData) => {
+ outputUiData = data;
+ });
+
+ expect(outputUiData).toEqual(UiData.EMPTY);
+
+ presenter.onTracePositionUpdate(TracePosition.fromTimestamp(new RealTimestamp(10n)));
+ expect(outputUiData).toEqual(UiData.EMPTY);
+ });
+
+ it('processes trace position update and computes output UI data', () => {
+ presenter.onTracePositionUpdate(createTracePosition(0));
+
+ expect(outputUiData!.allPids).toEqual([
+ 'N/A',
+ '0',
+ '515',
+ '1593',
+ '2022',
+ '2322',
+ '2463',
+ '3300',
+ ]);
+ expect(outputUiData!.allUids).toEqual(['N/A', '1000', '1003', '10169', '10235', '10239']);
+ expect(outputUiData!.allTypes).toEqual([
+ 'DISPLAY_CHANGED',
+ 'LAYER_ADDED',
+ 'LAYER_CHANGED',
+ 'LAYER_DESTROYED',
+ 'LAYER_HANDLE_DESTROYED',
+ 'NO_OP',
+ ]);
+
+ expect(outputUiData?.allTransactionIds.length).toEqual(1295);
+ expect(outputUiData?.allLayerAndDisplayIds.length).toEqual(117);
+
+ expect(outputUiData?.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
+
+ expect(outputUiData?.currentEntryIndex).toEqual(0);
+ expect(outputUiData?.selectedEntryIndex).toBeUndefined();
+ expect(outputUiData?.scrollToIndex).toEqual(0);
+ expect(outputUiData?.currentPropertiesTree).toBeDefined();
+ });
+
+ it('processes trace position update and updates current entry and scroll position', () => {
+ presenter.onTracePositionUpdate(createTracePosition(0));
+ expect(outputUiData!.currentEntryIndex).toEqual(0);
+ expect(outputUiData!.scrollToIndex).toEqual(0);
+
+ presenter.onTracePositionUpdate(createTracePosition(10));
+ expect(outputUiData!.currentEntryIndex).toEqual(13);
+ expect(outputUiData!.scrollToIndex).toEqual(13);
+ });
+
+ it('filters entries according to transaction ID filter', () => {
+ presenter.onIdFilterChanged('');
+ expect(outputUiData!.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
+
+ presenter.onIdFilterChanged('2211908157465');
+ expect(new Set(outputUiData!.entries.map((entry) => entry.transactionId))).toEqual(
+ new Set(['2211908157465'])
+ );
+ });
+
+ it('filters entries according to VSYNC ID filter', () => {
+ presenter.onVSyncIdFilterChanged([]);
+ expect(outputUiData!.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
+
+ presenter.onVSyncIdFilterChanged(['1']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.vsyncId))).toEqual(new Set([1]));
+
+ presenter.onVSyncIdFilterChanged(['1', '3', '10']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.vsyncId))).toEqual(
+ new Set([1, 3, 10])
+ );
+ });
+
+ it('filters entries according to PID filter', () => {
+ presenter.onPidFilterChanged([]);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.pid))).toEqual(
+ new Set(['N/A', '0', '515', '1593', '2022', '2322', '2463', '3300'])
+ );
+
+ presenter.onPidFilterChanged(['0']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.pid))).toEqual(new Set(['0']));
+
+ presenter.onPidFilterChanged(['0', '515']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.pid))).toEqual(new Set(['0', '515']));
+ });
+
+ it('filters entries according to UID filter', () => {
+ presenter.onUidFilterChanged([]);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.uid))).toEqual(
+ new Set(['N/A', '1000', '1003', '10169', '10235', '10239'])
+ );
+
+ presenter.onUidFilterChanged(['1000']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.uid))).toEqual(new Set(['1000']));
+
+ presenter.onUidFilterChanged(['1000', '1003']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.uid))).toEqual(
+ new Set(['1000', '1003'])
+ );
+ });
+
+ it('filters entries according to type filter', () => {
+ presenter.onTypeFilterChanged([]);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.type))).toEqual(
+ new Set([
+ UiDataEntryType.DISPLAY_CHANGED,
+ UiDataEntryType.LAYER_ADDED,
+ UiDataEntryType.LAYER_CHANGED,
+ UiDataEntryType.LAYER_DESTROYED,
+ UiDataEntryType.LAYER_HANDLE_DESTROYED,
+ UiDataEntryType.NO_OP,
+ ])
+ );
+
+ presenter.onTypeFilterChanged([UiDataEntryType.LAYER_ADDED]);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.type))).toEqual(
+ new Set([UiDataEntryType.LAYER_ADDED])
+ );
+
+ presenter.onTypeFilterChanged([UiDataEntryType.LAYER_ADDED, UiDataEntryType.LAYER_DESTROYED]);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.type))).toEqual(
+ new Set([UiDataEntryType.LAYER_ADDED, UiDataEntryType.LAYER_DESTROYED])
+ );
+ });
+
+ it('filters entries according to layer or display ID filter', () => {
+ presenter.onLayerIdFilterChanged([]);
+ expect(
+ new Set(outputUiData!.entries.map((entry) => entry.layerOrDisplayId)).size
+ ).toBeGreaterThan(20);
+
+ presenter.onLayerIdFilterChanged(['1']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.layerOrDisplayId))).toEqual(
+ new Set(['1'])
+ );
+
+ presenter.onLayerIdFilterChanged(['1', '3']);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.layerOrDisplayId))).toEqual(
+ new Set(['1', '3'])
+ );
+ });
+
+ it('includes no op transitions', () => {
+ presenter.onTypeFilterChanged([UiDataEntryType.NO_OP]);
+ expect(new Set(outputUiData!.entries.map((entry) => entry.type))).toEqual(
+ new Set([UiDataEntryType.NO_OP])
+ );
+
+ for (const entry of outputUiData!.entries) {
+ expect(entry.layerOrDisplayId).toEqual('');
+ expect(entry.what).toEqual('');
+ expect(entry.propertiesTree).toEqual({});
+ }
+ });
+
+ it('filters entries according to "what" search string', () => {
+ expect(outputUiData!.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
+
+ presenter.onWhatSearchStringChanged('');
+ expect(outputUiData!.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
+
+ presenter.onWhatSearchStringChanged('Crop');
+ expect(outputUiData!.entries.length).toBeLessThan(TOTAL_OUTPUT_ENTRIES);
+
+ presenter.onWhatSearchStringChanged('STRING_WITH_NO_MATCHES');
+ expect(outputUiData!.entries.length).toEqual(0);
+ });
+
+ it('updates selected entry and properties tree when entry is clicked', () => {
+ presenter.onTracePositionUpdate(createTracePosition(0));
+ expect(outputUiData!.currentEntryIndex).toEqual(0);
+ expect(outputUiData!.selectedEntryIndex).toBeUndefined();
+ expect(outputUiData!.scrollToIndex).toEqual(0);
+ expect(outputUiData!.currentPropertiesTree).toEqual(outputUiData!.entries[0].propertiesTree);
+
+ presenter.onEntryClicked(10);
+ expect(outputUiData!.currentEntryIndex).toEqual(0);
+ expect(outputUiData!.selectedEntryIndex).toEqual(10);
+ expect(outputUiData!.scrollToIndex).toBeUndefined(); // no scrolling
+ expect(outputUiData!.currentPropertiesTree).toEqual(outputUiData!.entries[10].propertiesTree);
+
+ // remove selection when selected entry is clicked again
+ presenter.onEntryClicked(10);
+ expect(outputUiData!.currentEntryIndex).toEqual(0);
+ expect(outputUiData!.selectedEntryIndex).toBeUndefined();
+ expect(outputUiData!.scrollToIndex).toBeUndefined(); // no scrolling
+ expect(outputUiData!.currentPropertiesTree).toEqual(outputUiData!.entries[0].propertiesTree);
+ });
+
+ it('computes current entry index', () => {
+ presenter.onTracePositionUpdate(createTracePosition(0));
+ expect(outputUiData!.currentEntryIndex).toEqual(0);
+
+ presenter.onTracePositionUpdate(createTracePosition(10));
+ expect(outputUiData!.currentEntryIndex).toEqual(13);
+ });
+
+ it('updates current entry index when filters change', () => {
+ presenter.onTracePositionUpdate(createTracePosition(10));
+
+ presenter.onPidFilterChanged([]);
+ expect(outputUiData!.currentEntryIndex).toEqual(13);
+
+ presenter.onPidFilterChanged(['0']);
+ expect(outputUiData!.currentEntryIndex).toEqual(10);
+
+ presenter.onPidFilterChanged(['0', '515']);
+ expect(outputUiData!.currentEntryIndex).toEqual(11);
+
+ presenter.onPidFilterChanged(['0', '515', 'N/A']);
+ expect(outputUiData!.currentEntryIndex).toEqual(13);
+ });
+
+ it('formats real time', () => {
+ setUpTestEnvironment(TimestampType.REAL);
+ expect(outputUiData!.entries[0].time).toEqual('2022-08-03T06:19:01.051480997');
+ });
+
+ it('formats elapsed time', () => {
+ setUpTestEnvironment(TimestampType.ELAPSED);
+ expect(outputUiData!.entries[0].time).toEqual('2s450ms981445ns');
+ });
+
+ const setUpTestEnvironment = (timestampType: TimestampType) => {
+ trace = new TraceBuilder<object>().setParser(parser).setTimestampType(timestampType).build();
+
+ traces = new Traces();
+ traces.setTrace(TraceType.TRANSACTIONS, trace);
+
+ presenter = new Presenter(traces, (data: UiData) => {
+ outputUiData = data;
+ });
+ };
+
+ const createTracePosition = (entryIndex: number): TracePosition => {
+ return TracePosition.fromTraceEntry(trace.getEntry(entryIndex));
+ };
+});
diff --git a/tools/winscope/src/viewers/viewer_transactions/ui_data.ts b/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
new file mode 100644
index 0000000..f61d86c
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 {PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+
+class UiData {
+ constructor(
+ public allVSyncIds: string[],
+ public allPids: string[],
+ public allUids: string[],
+ public allTypes: string[],
+ public allLayerAndDisplayIds: string[],
+ public allTransactionIds: string[],
+ public entries: UiDataEntry[],
+ public currentEntryIndex: undefined | number,
+ public selectedEntryIndex: undefined | number,
+ public scrollToIndex: undefined | number,
+ public currentPropertiesTree: undefined | PropertiesTreeNode
+ ) {}
+
+ static EMPTY = new UiData([], [], [], [], [], [], [], undefined, undefined, undefined, undefined);
+}
+
+class UiDataEntry {
+ constructor(
+ public originalIndexInTraceEntry: number,
+ public time: string,
+ public vsyncId: number,
+ public pid: string,
+ public uid: string,
+ public type: string,
+ public layerOrDisplayId: string,
+ public transactionId: string,
+ public what: string,
+ public propertiesTree?: PropertiesTreeNode
+ ) {}
+}
+
+class UiDataEntryType {
+ static DISPLAY_ADDED = 'DISPLAY_ADDED';
+ static DISPLAY_REMOVED = 'DISPLAY_REMOVED';
+ static DISPLAY_CHANGED = 'DISPLAY_CHANGED';
+ static LAYER_ADDED = 'LAYER_ADDED';
+ static LAYER_DESTROYED = 'LAYER_DESTROYED';
+ static LAYER_CHANGED = 'LAYER_CHANGED';
+ static LAYER_HANDLE_DESTROYED = 'LAYER_HANDLE_DESTROYED';
+ static NO_OP = 'NO_OP';
+}
+
+export {UiData, UiDataEntry, UiDataEntryType};
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
new file mode 100644
index 0000000..7548f29
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+import {Events} from './events';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+class ViewerTransactions implements Viewer {
+ constructor(traces: Traces) {
+ this.htmlElement = document.createElement('viewer-transactions');
+
+ this.presenter = new Presenter(traces, (data: UiData) => {
+ (this.htmlElement as any).inputData = data;
+ });
+
+ this.htmlElement.addEventListener(Events.VSyncIdFilterChanged, (event) => {
+ this.presenter.onVSyncIdFilterChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.PidFilterChanged, (event) => {
+ this.presenter.onPidFilterChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.UidFilterChanged, (event) => {
+ this.presenter.onUidFilterChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.TypeFilterChanged, (event) => {
+ this.presenter.onTypeFilterChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.LayerIdFilterChanged, (event) => {
+ this.presenter.onLayerIdFilterChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.WhatSearchStringChanged, (event) => {
+ this.presenter.onWhatSearchStringChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.IdFilterChanges, (event) => {
+ this.presenter.onIdFilterChanged((event as CustomEvent).detail);
+ });
+
+ this.htmlElement.addEventListener(Events.EntryClicked, (event) => {
+ this.presenter.onEntryClicked((event as CustomEvent).detail);
+ });
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.presenter.onTracePositionUpdate(position);
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Transactions',
+ TraceType.TRANSACTIONS
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return ViewerTransactions.DEPENDENCIES;
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.TRANSACTIONS];
+ private htmlElement: HTMLElement;
+ private presenter: Presenter;
+}
+
+export {ViewerTransactions};
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts
new file mode 100644
index 0000000..0158bd0
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2022 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 {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
+import {Component, ElementRef, Inject, Input, ViewChild} from '@angular/core';
+import {MatSelectChange} from '@angular/material/select';
+import {Events} from './events';
+import {UiData} from './ui_data';
+
+@Component({
+ selector: 'viewer-transactions',
+ template: `
+ <div class="card-grid">
+ <div class="entries">
+ <div class="filters">
+ <div class="time"></div>
+ <div class="id">
+ <mat-form-field appearance="fill">
+ <mat-label>TX ID</mat-label>
+ <input matInput [(ngModel)]="idString" (input)="onIdSearchStringChanged()" />
+ </mat-form-field>
+ </div>
+ <div class="vsyncid">
+ <mat-form-field appearance="fill">
+ <mat-label>VSYNC ID</mat-label>
+ <mat-select (selectionChange)="onVSyncIdFilterChanged($event)" multiple>
+ <mat-option *ngFor="let vsyncId of uiData.allVSyncIds" [value]="vsyncId">
+ {{ vsyncId }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="pid">
+ <mat-form-field appearance="fill">
+ <mat-label>PID</mat-label>
+ <mat-select (selectionChange)="onPidFilterChanged($event)" multiple>
+ <mat-option *ngFor="let pid of uiData.allPids" [value]="pid">
+ {{ pid }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="uid">
+ <mat-form-field appearance="fill">
+ <mat-label>UID</mat-label>
+ <mat-select (selectionChange)="onUidFilterChanged($event)" multiple>
+ <mat-option *ngFor="let uid of uiData.allUids" [value]="uid">
+ {{ uid }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="type">
+ <mat-form-field appearance="fill">
+ <mat-label>Type</mat-label>
+ <mat-select (selectionChange)="onTypeFilterChanged($event)" multiple>
+ <mat-option *ngFor="let type of uiData.allTypes" [value]="type">
+ {{ type }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="id">
+ <mat-form-field appearance="fill">
+ <mat-label>LAYER/DISP ID</mat-label>
+ <mat-select (selectionChange)="onLayerIdFilterChanged($event)" multiple>
+ <mat-option *ngFor="let id of uiData.allLayerAndDisplayIds" [value]="id">
+ {{ id }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="what">
+ <mat-form-field appearance="fill">
+ <mat-label>Search text</mat-label>
+ <input matInput [(ngModel)]="whatSearchString" (input)="onWhatSearchStringChange()" />
+ </mat-form-field>
+ </div>
+ </div>
+
+ <cdk-virtual-scroll-viewport itemSize="24" class="scroll">
+ <div
+ *cdkVirtualFor="let entry of uiData.entries; let i = index"
+ class="entry"
+ [class.current-entry]="isCurrentEntry(i)"
+ [class.selected-entry]="isSelectedEntry(i)"
+ (click)="onEntryClicked(i)">
+ <div class="time">
+ <span class="mat-body-1">{{ entry.time }}</span>
+ </div>
+ <div class="id">
+ <span class="mat-body-1">{{ entry.transactionId }}</span>
+ </div>
+ <div class="vsyncid">
+ <span class="mat-body-1">{{ entry.vsyncId }}</span>
+ </div>
+ <div class="pid">
+ <span class="mat-body-1">{{ entry.pid }}</span>
+ </div>
+ <div class="uid">
+ <span class="mat-body-1">{{ entry.uid }}</span>
+ </div>
+ <div class="type">
+ <span class="mat-body-1">{{ entry.type }}</span>
+ </div>
+ <div class="id">
+ <span class="mat-body-1">{{ entry.layerOrDisplayId }}</span>
+ </div>
+ <div class="what">
+ <span class="mat-body-1">{{ entry.what }}</span>
+ </div>
+ </div>
+ </cdk-virtual-scroll-viewport>
+ </div>
+
+ <mat-divider [vertical]="true"></mat-divider>
+
+ <div class="container-properties">
+ <h3 class="properties-title mat-title">Properties - Proto Dump</h3>
+ <tree-view
+ *ngIf="uiData.currentPropertiesTree"
+ class="properties-tree"
+ [item]="uiData.currentPropertiesTree"></tree-view>
+ </div>
+ </div>
+ `,
+ styles: [
+ `
+ .entries {
+ flex: 3;
+ display: flex;
+ flex-direction: column;
+ padding: 16px;
+ }
+
+ .container-properties {
+ flex: 1;
+ padding: 16px;
+ }
+
+ .entries .filters {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .entries .scroll {
+ flex: 1;
+ height: 100%;
+ }
+
+ .scroll .entry {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .filters div {
+ flex: 1;
+ padding: 4px;
+ }
+
+ .filters .vsyncid mat-form-field {
+ width: 120px;
+ }
+
+ .filters div.time {
+ flex: 2;
+ }
+
+ .filters div.what {
+ flex: 3;
+ }
+
+ .filters .id mat-form-field {
+ width: 150px;
+ }
+
+ .filters .what {
+ margin-right: 16px;
+ }
+
+ .filters .what mat-form-field {
+ width: 250px;
+ }
+
+ .entry div {
+ flex: 1;
+ padding: 4px;
+ }
+
+ .entry div.time {
+ flex: 2;
+ }
+
+ .entry div.what {
+ flex: 3;
+ }
+
+ .entry.current-entry {
+ color: white;
+ background-color: #365179;
+ }
+
+ .entry.selected-entry {
+ color: white;
+ background-color: #98aecd;
+ }
+
+ mat-form-field {
+ width: 100px;
+ }
+
+ ::ng-deep .mat-select-panel-wrap {
+ overflow: scroll;
+ overflow-x: hidden;
+ max-height: 75vh;
+ }
+ `,
+ ],
+})
+class ViewerTransactionsComponent {
+ uiData: UiData = UiData.EMPTY;
+ idString = '';
+ whatSearchString = '';
+
+ @ViewChild(CdkVirtualScrollViewport) private scrollComponent?: CdkVirtualScrollViewport;
+ private elementRef: ElementRef;
+
+ constructor(@Inject(ElementRef) elementRef: ElementRef) {
+ this.elementRef = elementRef;
+ }
+
+ @Input()
+ set inputData(data: UiData) {
+ this.uiData = data;
+ if (this.uiData.scrollToIndex !== undefined && this.scrollComponent) {
+ this.scrollComponent.scrollToIndex(this.uiData.scrollToIndex);
+ }
+ }
+
+ onVSyncIdFilterChanged(event: MatSelectChange) {
+ this.emitEvent(Events.VSyncIdFilterChanged, event.value);
+ }
+
+ onPidFilterChanged(event: MatSelectChange) {
+ this.emitEvent(Events.PidFilterChanged, event.value);
+ }
+
+ onUidFilterChanged(event: MatSelectChange) {
+ this.emitEvent(Events.UidFilterChanged, event.value);
+ }
+
+ onTypeFilterChanged(event: MatSelectChange) {
+ this.emitEvent(Events.TypeFilterChanged, event.value);
+ }
+
+ onLayerIdFilterChanged(event: MatSelectChange) {
+ this.emitEvent(Events.LayerIdFilterChanged, event.value);
+ }
+
+ onWhatSearchStringChange() {
+ this.emitEvent(Events.WhatSearchStringChanged, this.whatSearchString);
+ }
+
+ onIdSearchStringChanged() {
+ this.emitEvent(Events.IdFilterChanges, this.idString);
+ }
+
+ onEntryClicked(index: number) {
+ this.emitEvent(Events.EntryClicked, index);
+ }
+
+ isCurrentEntry(index: number): boolean {
+ return index === this.uiData.currentEntryIndex;
+ }
+
+ isSelectedEntry(index: number): boolean {
+ return index === this.uiData.selectedEntryIndex;
+ }
+
+ private emitEvent(event: string, data: any) {
+ const customEvent = new CustomEvent(event, {
+ bubbles: true,
+ detail: data,
+ });
+ this.elementRef.nativeElement.dispatchEvent(customEvent);
+ }
+}
+
+export {ViewerTransactionsComponent};
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
new file mode 100644
index 0000000..ba2dd6d
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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 {ScrollingModule} from '@angular/cdk/scrolling';
+import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {PropertiesTreeGenerator} from 'viewers/common/properties_tree_generator';
+import {UiData, UiDataEntry} from './ui_data';
+import {ViewerTransactionsComponent} from './viewer_transactions_component';
+
+describe('ViewerTransactionsComponent', () => {
+ let fixture: ComponentFixture<ViewerTransactionsComponent>;
+ let component: ViewerTransactionsComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [MatDividerModule, ScrollingModule],
+ declarations: [ViewerTransactionsComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(ViewerTransactionsComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+
+ component.uiData = await makeUiData();
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('renders filters', () => {
+ expect(htmlElement.querySelector('.entries .filters .pid')).toBeTruthy();
+ expect(htmlElement.querySelector('.entries .filters .uid')).toBeTruthy();
+ expect(htmlElement.querySelector('.entries .filters .type')).toBeTruthy();
+ expect(htmlElement.querySelector('.entries .filters .id')).toBeTruthy();
+ });
+
+ it('renders entries', () => {
+ expect(htmlElement.querySelector('.scroll')).toBeTruthy();
+
+ const entry = htmlElement.querySelector('.scroll .entry');
+ expect(entry).toBeTruthy();
+ expect(entry!.innerHTML).toContain('TIME_VALUE');
+ expect(entry!.innerHTML).toContain('-111');
+ expect(entry!.innerHTML).toContain('PID_VALUE');
+ expect(entry!.innerHTML).toContain('UID_VALUE');
+ expect(entry!.innerHTML).toContain('TYPE_VALUE');
+ expect(entry!.innerHTML).toContain('ID_VALUE');
+ expect(entry!.innerHTML).toContain('flag1 | flag2 | ...');
+ });
+
+ it('renders properties', () => {
+ expect(htmlElement.querySelector('.properties-tree')).toBeTruthy();
+ });
+});
+
+async function makeUiData(): Promise<UiData> {
+ const propertiesTree = new PropertiesTreeGenerator().generate('ROOT', {KEY: 'VALUE'});
+
+ const entry = new UiDataEntry(
+ 0,
+ 'TIME_VALUE',
+ -111,
+ 'PID_VALUE',
+ 'UID_VALUE',
+ 'TYPE_VALUE',
+ 'LAYER_OR_DISPLAY_ID_VALUE',
+ 'TRANSACTION_ID_VALUE',
+ 'flag1 | flag2 | ...',
+ propertiesTree
+ );
+
+ return new UiData([], [], [], [], [], [], [entry], 0, 0, 0, propertiesTree);
+}
diff --git a/tools/winscope/src/stubs/waylandtrace.proto b/tools/winscope/src/viewers/viewer_transitions/events.ts
similarity index 76%
copy from tools/winscope/src/stubs/waylandtrace.proto
copy to tools/winscope/src/viewers/viewer_transitions/events.ts
index bfe19ce..95f9665 100644
--- a/tools/winscope/src/stubs/waylandtrace.proto
+++ b/tools/winscope/src/viewers/viewer_transitions/events.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-package org.chromium.arc.wayland_composer;
+class Events {
+ static TransitionSelected = 'ViewerTransitionsEvent_TransitionSelected';
+}
-message TraceFileProto {}
-message OutputStateProto {}
\ No newline at end of file
+export {Events};
diff --git a/tools/winscope/src/viewers/viewer_transitions/presenter.ts b/tools/winscope/src/viewers/viewer_transitions/presenter.ts
new file mode 100644
index 0000000..6a88bf6
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transitions/presenter.ts
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2023 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 {assertDefined} from 'common/assert_utils';
+import {TimeUtils} from 'common/time_utils';
+import {LayerTraceEntry, Transition, WindowManagerState} from 'trace/flickerlib/common';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UiData} from './ui_data';
+
+export class Presenter {
+ constructor(traces: Traces, notifyUiDataCallback: (data: UiData) => void) {
+ this.transitionTrace = assertDefined(traces.getTrace(TraceType.TRANSITION));
+ this.surfaceFlingerTrace = traces.getTrace(TraceType.SURFACE_FLINGER);
+ this.windowManagerTrace = traces.getTrace(TraceType.WINDOW_MANAGER);
+ this.notifyUiDataCallback = notifyUiDataCallback;
+ this.uiData = this.computeUiData();
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onTracePositionUpdate(position: TracePosition): void {
+ const entry = TraceEntryFinder.findCorrespondingEntry(this.transitionTrace, position);
+
+ this.uiData.selectedTransition = entry?.getValue();
+
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ onTransitionSelected(transition: Transition): void {
+ this.uiData.selectedTransition = transition;
+ this.uiData.selectedTransitionPropertiesTree =
+ this.makeSelectedTransitionPropertiesTree(transition);
+ this.notifyUiDataCallback(this.uiData);
+ }
+
+ private computeUiData(): UiData {
+ const transitions: Transition[] = [];
+
+ this.transitionTrace.forEachEntry((entry, originalIndex) => {
+ transitions.push(entry.getValue());
+ });
+
+ const selectedTransition = this.uiData?.selectedTransition ?? undefined;
+ const selectedTransitionPropertiesTree =
+ this.uiData?.selectedTransitionPropertiesTree ?? undefined;
+
+ const timestampType = this.transitionTrace.getTimestampType();
+ if (timestampType === undefined) {
+ throw new Error('Missing timestamp type in trace!');
+ }
+ return new UiData(
+ transitions,
+ selectedTransition,
+ timestampType,
+ selectedTransitionPropertiesTree
+ );
+ }
+
+ private makeSelectedTransitionPropertiesTree(transition: Transition): PropertiesTreeNode {
+ const changes: PropertiesTreeNode[] = [];
+
+ for (const change of transition.changes) {
+ let layerName: string | undefined = undefined;
+ let windowName: string | undefined = undefined;
+
+ if (this.surfaceFlingerTrace) {
+ this.surfaceFlingerTrace.forEachEntry((entry, originalIndex) => {
+ if (layerName !== undefined) {
+ return;
+ }
+ const layerTraceEntry = entry.getValue() as LayerTraceEntry;
+ for (const layer of layerTraceEntry.flattenedLayers) {
+ if (layer.id === change.layerId) {
+ layerName = layer.name;
+ }
+ }
+ });
+ }
+
+ if (this.windowManagerTrace) {
+ this.windowManagerTrace.forEachEntry((entry, originalIndex) => {
+ if (windowName !== undefined) {
+ return;
+ }
+ const wmState = entry.getValue() as WindowManagerState;
+ for (const window of wmState.windowContainers) {
+ if (window.token.toLowerCase() === change.windowId.toString(16).toLowerCase()) {
+ windowName = window.title;
+ }
+ }
+ });
+ }
+
+ const layerIdValue = layerName ? `${change.layerId} (${layerName})` : change.layerId;
+ const windowIdValue = windowName
+ ? `0x${change.windowId.toString(16)} (${windowName})`
+ : `0x${change.windowId.toString(16)}`;
+
+ changes.push({
+ propertyKey: 'change',
+ children: [
+ {propertyKey: 'transitMode', propertyValue: change.transitMode},
+ {propertyKey: 'layerId', propertyValue: layerIdValue},
+ {propertyKey: 'windowId', propertyValue: windowIdValue},
+ ],
+ });
+ }
+
+ const properties: PropertiesTreeNode[] = [
+ {propertyKey: 'id', propertyValue: transition.id},
+ {propertyKey: 'type', propertyValue: transition.type},
+ {propertyKey: 'aborted', propertyValue: `${transition.aborted}`},
+ ];
+
+ if (transition.handler) {
+ properties.push({propertyKey: 'handler', propertyValue: transition.handler});
+ }
+
+ const timestampType = this.transitionTrace.getTimestampType();
+
+ if (!transition.createTime.isMin) {
+ properties.push({
+ propertyKey: 'createTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(transition.createTime, timestampType),
+ });
+ }
+
+ if (!transition.sendTime.isMin) {
+ properties.push({
+ propertyKey: 'sendTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(transition.sendTime, timestampType),
+ });
+ }
+
+ if (!transition.dispatchTime.isMin) {
+ properties.push({
+ propertyKey: 'dispatchTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(transition.dispatchTime, timestampType),
+ });
+ }
+
+ if (!transition.finishTime.isMax) {
+ properties.push({
+ propertyKey: 'finishTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(transition.finishTime, timestampType),
+ });
+ }
+
+ if (transition.mergeRequestTime) {
+ properties.push({
+ propertyKey: 'mergeRequestTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(
+ transition.mergeRequestTime,
+ timestampType
+ ),
+ });
+ }
+
+ if (transition.shellAbortTime) {
+ properties.push({
+ propertyKey: 'shellAbortTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(transition.shellAbortTime, timestampType),
+ });
+ }
+
+ if (transition.mergeTime) {
+ properties.push({
+ propertyKey: 'mergeTime',
+ propertyValue: TimeUtils.formattedKotlinTimestamp(transition.mergeTime, timestampType),
+ });
+ }
+
+ if (transition.mergedInto) {
+ properties.push({propertyKey: 'mergedInto', propertyValue: transition.mergedInto});
+ }
+
+ if (transition.startTransactionId !== -1) {
+ properties.push({
+ propertyKey: 'startTransactionId',
+ propertyValue: transition.startTransactionId,
+ });
+ }
+ if (transition.finishTransactionId !== -1) {
+ properties.push({
+ propertyKey: 'finishTransactionId',
+ propertyValue: transition.finishTransactionId,
+ });
+ }
+ if (changes.length > 0) {
+ properties.push({propertyKey: 'changes', children: changes});
+ }
+
+ return {
+ children: properties,
+ propertyKey: 'Selected Transition',
+ };
+ }
+
+ private transitionTrace: Trace<object>;
+ private surfaceFlingerTrace: Trace<object> | undefined;
+ private windowManagerTrace: Trace<object> | undefined;
+ private uiData: UiData;
+ private readonly notifyUiDataCallback: (data: UiData) => void;
+}
diff --git a/tools/winscope/src/viewers/viewer_transitions/ui_data.ts b/tools/winscope/src/viewers/viewer_transitions/ui_data.ts
new file mode 100644
index 0000000..8676b99
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transitions/ui_data.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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 {Transition} from 'trace/flickerlib/common';
+import {TimestampType} from 'trace/timestamp';
+import {PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+
+export class UiData {
+ constructor(
+ public entries: Transition[],
+ public selectedTransition: Transition,
+ public timestampType: TimestampType,
+ public selectedTransitionPropertiesTree?: PropertiesTreeNode
+ ) {}
+
+ static EMPTY = new UiData([], undefined, TimestampType.REAL, undefined);
+}
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts
new file mode 100644
index 0000000..6ba1e4e
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 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 {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+import {Events} from './events';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+export class ViewerTransitions implements Viewer {
+ constructor(traces: Traces) {
+ this.htmlElement = document.createElement('viewer-transitions');
+
+ this.presenter = new Presenter(traces, (data: UiData) => {
+ (this.htmlElement as any).inputData = data;
+ });
+
+ this.htmlElement.addEventListener(Events.TransitionSelected, (event) => {
+ this.presenter.onTransitionSelected((event as CustomEvent).detail);
+ });
+ }
+
+ onTracePositionUpdate(position: TracePosition): void {
+ this.presenter.onTracePositionUpdate(position);
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Transitions',
+ TraceType.TRANSITION
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return ViewerTransitions.DEPENDENCIES;
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.TRANSITION];
+ private htmlElement: HTMLElement;
+ private presenter: Presenter;
+}
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts
new file mode 100644
index 0000000..f642223
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2023 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 {Component, ElementRef, Inject, Input} from '@angular/core';
+import {TimeUtils} from 'common/time_utils';
+import {Transition} from 'trace/flickerlib/common';
+import {ElapsedTimestamp, TimestampType} from 'trace/timestamp';
+import {Terminal} from 'viewers/common/ui_tree_utils';
+import {Events} from './events';
+import {UiData} from './ui_data';
+
+@Component({
+ selector: 'viewer-transitions',
+ template: `
+ <div class="card-grid container">
+ <div class="top-viewer">
+ <div class="entries">
+ <div class="table-header table-row">
+ <div class="id">Id</div>
+ <div class="type">Type</div>
+ <div class="send-time">Send Time</div>
+ <div class="duration">Duration</div>
+ <div class="status">Status</div>
+ </div>
+ <cdk-virtual-scroll-viewport itemSize="53" class="scroll">
+ <div
+ *cdkVirtualFor="let transition of uiData.entries; let i = index"
+ class="entry table-row"
+ [class.current]="isCurrentTransition(transition)"
+ (click)="onTransitionClicked(transition)">
+ <div class="id">
+ <span class="mat-body-1">{{ transition.id }}</span>
+ </div>
+ <div class="type">
+ <span class="mat-body-1">{{ transition.type }}</span>
+ </div>
+ <div class="send-time">
+ <span *ngIf="!transition.sendTime.isMin" class="mat-body-1">{{
+ formattedTime(transition.sendTime, uiData.timestampType)
+ }}</span>
+ <span *ngIf="transition.sendTime.isMin"> n/a </span>
+ </div>
+ <div class="duration">
+ <span
+ *ngIf="!transition.sendTime.isMin && !transition.finishTime.isMax"
+ class="mat-body-1"
+ >{{
+ formattedTimeDiff(
+ transition.sendTime,
+ transition.finishTime,
+ uiData.timestampType
+ )
+ }}</span
+ >
+ <span *ngIf="transition.sendTime.isMin || transition.finishTime.isMax">n/a</span>
+ </div>
+ <div class="status">
+ <div *ngIf="transition.mergedInto">
+ <span>MERGED</span>
+ <mat-icon aria-hidden="false" fontIcon="merge" matTooltip="merged" icon-gray>
+ </mat-icon>
+ </div>
+
+ <div *ngIf="transition.aborted && !transition.mergedInto">
+ <span>ABORTED</span>
+ <mat-icon
+ aria-hidden="false"
+ fontIcon="close"
+ matTooltip="aborted"
+ style="color: red"
+ icon-red></mat-icon>
+ </div>
+
+ <div *ngIf="transition.played && !transition.aborted && !transition.mergedInto">
+ <span>PLAYED</span>
+ <mat-icon
+ aria-hidden="false"
+ fontIcon="check"
+ matTooltip="played"
+ style="color: green"
+ *ngIf="
+ transition.played && !transition.aborted && !transition.mergedInto
+ "></mat-icon>
+ </div>
+ </div>
+ </div>
+ </cdk-virtual-scroll-viewport>
+ </div>
+
+ <mat-divider [vertical]="true"></mat-divider>
+
+ <div class="container-properties">
+ <h3 class="properties-title mat-title">Selected Transition</h3>
+ <tree-view
+ [item]="uiData.selectedTransitionPropertiesTree"
+ [showNode]="showNode"
+ [isLeaf]="isLeaf"
+ [isAlwaysCollapsed]="true">
+ </tree-view>
+ <div *ngIf="!uiData.selectedTransitionPropertiesTree">
+ No selected transition.<br />
+ Select the tranitions below.
+ </div>
+ </div>
+ </div>
+
+ <div class="bottom-viewer">
+ <div class="transition-timeline">
+ <div *ngFor="let row of timelineRows()" class="row">
+ <svg width="100%" [attr.height]="transitionHeight">
+ <rect
+ *ngFor="let transition of transitionsOnRow(row)"
+ [attr.width]="widthOf(transition)"
+ [attr.height]="transitionHeight"
+ [attr.style]="transitionRectStyle(transition)"
+ rx="5"
+ [attr.x]="startOf(transition)"
+ (click)="onTransitionClicked(transition)" />
+ <rect
+ *ngFor="let transition of transitionsOnRow(row)"
+ [attr.width]="transitionDividerWidth"
+ [attr.height]="transitionHeight"
+ [attr.style]="transitionDividerRectStyle(transition)"
+ [attr.x]="sendOf(transition)" />
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+ `,
+ styles: [
+ `
+ .container {
+ display: flex;
+ flex-grow: 1;
+ flex-direction: column;
+ }
+
+ .top-viewer {
+ display: flex;
+ flex-grow: 1;
+ flex: 3;
+ border-bottom: solid 1px rgba(0, 0, 0, 0.12);
+ }
+
+ .bottom-viewer {
+ display: flex;
+ flex-shrink: 1;
+ }
+
+ .transition-timeline {
+ flex-grow: 1;
+ padding: 1.5rem 1rem;
+ }
+
+ .entries {
+ flex: 3;
+ display: flex;
+ flex-direction: column;
+ padding: 16px;
+ }
+
+ .container-properties {
+ flex: 1;
+ padding: 16px;
+ }
+
+ .entries .scroll {
+ height: 100%;
+ }
+
+ .entries .table-header {
+ flex: 1;
+ }
+
+ .table-row {
+ display: flex;
+ flex-direction: row;
+ cursor: pointer;
+ border-bottom: solid 1px rgba(0, 0, 0, 0.12);
+ }
+
+ .table-header.table-row {
+ font-weight: bold;
+ border-bottom: solid 1px rgba(0, 0, 0, 0.5);
+ }
+
+ .scroll .entry.current {
+ color: white;
+ background-color: #365179;
+ }
+
+ .table-row > div {
+ padding: 16px;
+ }
+
+ .table-row .id {
+ flex: 1;
+ }
+
+ .table-row .type {
+ flex: 2;
+ }
+
+ .table-row .send-time {
+ flex: 4;
+ }
+
+ .table-row .duration {
+ flex: 3;
+ }
+
+ .table-row .status {
+ flex: 2;
+ }
+
+ .status > div {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 5px;
+ }
+
+ .current .status mat-icon {
+ color: white !important;
+ }
+
+ .transition-timeline .row svg rect {
+ cursor: pointer;
+ }
+
+ .label {
+ width: 300px;
+ padding: 1rem;
+ }
+
+ .lines {
+ flex-grow: 1;
+ padding: 0.5rem;
+ }
+
+ .selected-transition {
+ padding: 1rem;
+ border-bottom: solid 1px rgba(0, 0, 0, 0.12);
+ flex-grow: 1;
+ }
+ `,
+ ],
+})
+export class ViewerTransitionsComponent {
+ transitionHeight = '20px';
+ transitionDividerWidth = '3px';
+
+ constructor(@Inject(ElementRef) elementRef: ElementRef) {
+ this.elementRef = elementRef;
+ }
+
+ @Input()
+ set inputData(data: UiData) {
+ this.uiData = data;
+ }
+
+ getMinOfRanges(): bigint {
+ if (this.uiData.entries.length === 0) {
+ return 0n;
+ }
+ const minOfRange = bigIntMin(
+ ...this.uiData.entries
+ .filter((it) => !it.createTime.isMin)
+ .map((it) => BigInt(it.createTime.elapsedNanos.toString()))
+ );
+ return minOfRange;
+ }
+
+ getMaxOfRanges(): bigint {
+ if (this.uiData.entries.length === 0) {
+ return 0n;
+ }
+ const maxOfRange = bigIntMax(
+ ...this.uiData.entries
+ .filter((it) => !it.finishTime.isMax)
+ .map((it) => BigInt(it.finishTime.elapsedNanos.toString()))
+ );
+ return maxOfRange;
+ }
+
+ formattedTime(time: any, timestampType: TimestampType): string {
+ return TimeUtils.formattedKotlinTimestamp(time, timestampType);
+ }
+
+ formattedTimeDiff(time1: any, time2: any, timestampType: TimestampType): string {
+ const timeDiff = new ElapsedTimestamp(
+ BigInt(time2.elapsedNanos.toString()) - BigInt(time1.elapsedNanos.toString())
+ );
+ return TimeUtils.format(timeDiff);
+ }
+
+ widthOf(transition: Transition) {
+ const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
+
+ let finish = BigInt(transition.finishTime.elapsedNanos.toString());
+ if (transition.finishTime.elapsedNanos.isMax) {
+ finish = this.getMaxOfRanges();
+ }
+
+ let start = BigInt(transition.createTime.elapsedNanos.toString());
+ if (transition.createTime.elapsedNanos.isMin) {
+ start = this.getMinOfRanges();
+ }
+
+ const minWidthPercent = 0.5;
+ return `${Math.max(minWidthPercent, Number((finish - start) * 100n) / Number(fullRange))}%`;
+ }
+
+ startOf(transition: Transition) {
+ const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
+ return `${
+ Number(
+ (BigInt(transition.createTime.elapsedNanos.toString()) - this.getMinOfRanges()) * 100n
+ ) / Number(fullRange)
+ }%`;
+ }
+
+ sendOf(transition: Transition) {
+ const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
+ return `${
+ Number((BigInt(transition.sendTime.elapsedNanos.toString()) - this.getMinOfRanges()) * 100n) /
+ Number(fullRange)
+ }%`;
+ }
+
+ onTransitionClicked(transition: Transition): void {
+ this.emitEvent(Events.TransitionSelected, transition);
+ }
+
+ transitionRectStyle(transition: Transition): string {
+ if (this.uiData.selectedTransition === transition) {
+ return 'fill:rgb(0, 0, 230)';
+ } else if (transition.aborted) {
+ return 'fill:rgb(255, 0, 0)';
+ } else {
+ return 'fill:rgb(78, 205, 230)';
+ }
+ }
+
+ transitionDividerRectStyle(transition: Transition): string {
+ return 'fill:rgb(255, 0, 0)';
+ }
+
+ showNode(item: any) {
+ return (
+ !(item instanceof Terminal) &&
+ !(item.name instanceof Terminal) &&
+ !(item.propertyKey instanceof Terminal)
+ );
+ }
+
+ isLeaf(item: any) {
+ return (
+ !item.children ||
+ item.children.length === 0 ||
+ item.children.filter((c: any) => !(c instanceof Terminal)).length === 0
+ );
+ }
+
+ isCurrentTransition(transition: Transition): boolean {
+ return this.uiData.selectedTransition === transition;
+ }
+
+ assignRowsToTransitions(): Map<Transition, number> {
+ const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
+ const assignedRows = new Map<Transition, number>();
+
+ const sortedTransitions = [...this.uiData.entries].sort((t1, t2) => {
+ const diff =
+ BigInt(t1.createTime.elapsedNanos.toString()) -
+ BigInt(t2.createTime.elapsedNanos.toString());
+ if (diff < 0) {
+ return -1;
+ }
+ if (diff > 0) {
+ return 1;
+ }
+ return 0;
+ });
+
+ const rowFirstAvailableTime = new Map<number, bigint>();
+ let rowsUsed = 1;
+ rowFirstAvailableTime.set(0, 0n);
+
+ for (const transition of sortedTransitions) {
+ const start = BigInt(transition.createTime.elapsedNanos.toString());
+ const end = BigInt(transition.finishTime.elapsedNanos.toString());
+
+ let rowIndexWithSpace = undefined;
+ for (let rowIndex = 0; rowIndex < rowsUsed; rowIndex++) {
+ if (start > rowFirstAvailableTime.get(rowIndex)!) {
+ // current row has space
+ rowIndexWithSpace = rowIndex;
+ break;
+ }
+ }
+
+ if (rowIndexWithSpace === undefined) {
+ rowIndexWithSpace = rowsUsed;
+ rowsUsed++;
+ }
+
+ assignedRows.set(transition, rowIndexWithSpace);
+
+ const minimumPaddingBetweenEntries = fullRange / 100n;
+
+ rowFirstAvailableTime.set(rowIndexWithSpace, end + minimumPaddingBetweenEntries);
+ }
+
+ return assignedRows;
+ }
+
+ timelineRows(): number[] {
+ return [...new Set(this.assignRowsToTransitions().values())];
+ }
+
+ transitionsOnRow(row: number): Transition[] {
+ const transitions = [];
+ const assignedRows = this.assignRowsToTransitions();
+
+ for (const transition of assignedRows.keys()) {
+ if (row === assignedRows.get(transition)) {
+ transitions.push(transition);
+ }
+ }
+
+ return transitions;
+ }
+
+ rowsRequiredForTransitions(): number {
+ return Math.max(...this.assignRowsToTransitions().values());
+ }
+
+ private emitEvent(event: string, data: any) {
+ const customEvent = new CustomEvent(event, {
+ bubbles: true,
+ detail: data,
+ });
+ this.elementRef.nativeElement.dispatchEvent(customEvent);
+ }
+
+ uiData: UiData = UiData.EMPTY;
+ private elementRef: ElementRef;
+}
+
+const bigIntMax = (...args: Array<bigint>) => args.reduce((m, e) => (e > m ? e : m));
+const bigIntMin = (...args: Array<bigint>) => args.reduce((m, e) => (e < m ? e : m));
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
new file mode 100644
index 0000000..feb61c5
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 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 {ScrollingModule} from '@angular/cdk/scrolling';
+import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {
+ CrossPlatform,
+ ShellTransitionData,
+ Transition,
+ TransitionChange,
+ TransitionType,
+ WmTransitionData,
+} from 'trace/flickerlib/common';
+import {TimestampType} from 'trace/timestamp';
+import {UiData} from './ui_data';
+import {ViewerTransitionsComponent} from './viewer_transitions_component';
+
+describe('ViewerTransitionsComponent', () => {
+ let fixture: ComponentFixture<ViewerTransitionsComponent>;
+ let component: ViewerTransitionsComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [MatDividerModule, ScrollingModule],
+ declarations: [ViewerTransitionsComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(ViewerTransitionsComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+
+ component.uiData = makeUiData();
+ fixture.detectChanges();
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('renders entries', () => {
+ expect(htmlElement.querySelector('.scroll')).toBeTruthy();
+
+ const entry = htmlElement.querySelector('.scroll .entry');
+ expect(entry).toBeTruthy();
+ expect(entry!.innerHTML).toContain('TO_FRONT');
+ expect(entry!.innerHTML).toContain('10ns');
+ });
+
+ it('shows message when no transition is selected', () => {
+ expect(htmlElement.querySelector('.container-properties')?.innerHTML).toContain(
+ 'No selected transition'
+ );
+ });
+});
+
+function makeUiData(): UiData {
+ const transitions = [
+ createMockTransition(10, 20, 30),
+ createMockTransition(40, 42, 50),
+ createMockTransition(45, 46, 49),
+ createMockTransition(55, 58, 70),
+ ];
+
+ const selectedTransition = undefined;
+ const selectedTransitionPropertiesTree = undefined;
+ const timestampType = TimestampType.REAL;
+
+ return new UiData(
+ transitions,
+ selectedTransition,
+ timestampType,
+ selectedTransitionPropertiesTree
+ );
+}
+
+function createMockTransition(
+ createTimeNanos: number,
+ sendTimeNanos: number,
+ finishTimeNanos: number
+): Transition {
+ const createTime = CrossPlatform.timestamp.fromString(createTimeNanos.toString(), null, null);
+ const sendTime = CrossPlatform.timestamp.fromString(sendTimeNanos.toString(), null, null);
+ const abortTime = null;
+ const finishTime = CrossPlatform.timestamp.fromString(finishTimeNanos.toString(), null, null);
+
+ const startTransactionId = '-1';
+ const finishTransactionId = '-1';
+ const type = TransitionType.TO_FRONT;
+ const changes: TransitionChange[] = [];
+
+ return new Transition(
+ id++,
+ new WmTransitionData(
+ createTime,
+ sendTime,
+ abortTime,
+ finishTime,
+ startTransactionId,
+ finishTransactionId,
+ type,
+ changes
+ ),
+ new ShellTransitionData()
+ );
+}
+
+let id = 0;
diff --git a/tools/winscope/src/viewers/viewer_window_manager/presenter.ts b/tools/winscope/src/viewers/viewer_window_manager/presenter.ts
new file mode 100644
index 0000000..f62a6fa
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_window_manager/presenter.ts
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2022 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 {assertDefined} from 'common/assert_utils';
+import {PersistentStoreProxy} from 'common/persistent_store_proxy';
+import {FilterType, TreeUtils} from 'common/tree_utils';
+import {DisplayContent} from 'trace/flickerlib/windows/DisplayContent';
+import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TraceEntryFinder} from 'trace/trace_entry_finder';
+import {TracePosition} from 'trace/trace_position';
+import {TraceTreeNode} from 'trace/trace_tree_node';
+import {TraceType} from 'trace/trace_type';
+import {Rectangle, RectMatrix, RectTransform} from 'viewers/common/rectangle';
+import {TreeGenerator} from 'viewers/common/tree_generator';
+import {TreeTransformer} from 'viewers/common/tree_transformer';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {UiData} from './ui_data';
+
+type NotifyViewCallbackType = (uiData: UiData) => void;
+
+export class Presenter {
+ private readonly trace: Trace<WindowManagerState>;
+ private readonly notifyViewCallback: NotifyViewCallbackType;
+ private uiData: UiData;
+ private hierarchyFilter: FilterType = TreeUtils.makeNodeFilter('');
+ private propertiesFilter: FilterType = TreeUtils.makeNodeFilter('');
+ private highlightedItems: string[] = [];
+ private displayIds: number[] = [];
+ private pinnedItems: HierarchyTreeNode[] = [];
+ private pinnedIds: string[] = [];
+ private selectedHierarchyTree: HierarchyTreeNode | null = null;
+ private previousEntry: TraceTreeNode | null = null;
+ private entry: TraceTreeNode | null = null;
+ private hierarchyUserOptions: UserOptions = PersistentStoreProxy.new<UserOptions>(
+ 'WmHierarchyOptions',
+ {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: false,
+ },
+ },
+ this.storage
+ );
+ private propertiesUserOptions: UserOptions = PersistentStoreProxy.new<UserOptions>(
+ 'WmPropertyOptions',
+ {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ showDefaults: {
+ name: 'Show defaults',
+ enabled: false,
+ tooltip: `
+ If checked, shows the value of all properties.
+ Otherwise, hides all properties whose value is
+ the default for its data type.
+ `,
+ },
+ },
+ this.storage
+ );
+
+ constructor(
+ traces: Traces,
+ private storage: Storage,
+ notifyViewCallback: NotifyViewCallbackType
+ ) {
+ this.trace = assertDefined(traces.getTrace(TraceType.WINDOW_MANAGER));
+ this.notifyViewCallback = notifyViewCallback;
+ this.uiData = new UiData([TraceType.WINDOW_MANAGER]);
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updatePinnedItems(pinnedItem: HierarchyTreeNode) {
+ const pinnedId = `${pinnedItem.id}`;
+ if (this.pinnedItems.map((item) => `${item.id}`).includes(pinnedId)) {
+ this.pinnedItems = this.pinnedItems.filter((pinned) => `${pinned.id}` !== pinnedId);
+ } else {
+ this.pinnedItems.push(pinnedItem);
+ }
+ this.updatePinnedIds(pinnedId);
+ this.uiData.pinnedItems = this.pinnedItems;
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updateHighlightedItems(id: string) {
+ if (this.highlightedItems.includes(id)) {
+ this.highlightedItems = this.highlightedItems.filter((hl) => hl !== id);
+ } else {
+ this.highlightedItems = []; //if multi-select implemented, remove this line
+ this.highlightedItems.push(id);
+ }
+ this.uiData.highlightedItems = this.highlightedItems;
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updateHierarchyTree(userOptions: UserOptions) {
+ this.hierarchyUserOptions = userOptions;
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.tree = this.generateTree();
+ this.notifyViewCallback(this.uiData);
+ }
+
+ filterHierarchyTree(filterString: string) {
+ this.hierarchyFilter = TreeUtils.makeNodeFilter(filterString);
+ this.uiData.tree = this.generateTree();
+ this.notifyViewCallback(this.uiData);
+ }
+
+ updatePropertiesTree(userOptions: UserOptions) {
+ this.propertiesUserOptions = userOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+ this.updateSelectedTreeUiData();
+ }
+
+ filterPropertiesTree(filterString: string) {
+ this.propertiesFilter = TreeUtils.makeNodeFilter(filterString);
+ this.updateSelectedTreeUiData();
+ }
+
+ newPropertiesTree(selectedTree: HierarchyTreeNode) {
+ this.selectedHierarchyTree = selectedTree;
+ this.updateSelectedTreeUiData();
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.uiData = new UiData();
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+
+ const entry = TraceEntryFinder.findCorrespondingEntry(this.trace, position);
+ const prevEntry =
+ entry && entry.getIndex() > 0 ? this.trace.getEntry(entry.getIndex() - 1) : undefined;
+
+ this.entry = entry?.getValue() ?? null;
+ this.previousEntry = prevEntry?.getValue() ?? null;
+ if (this.entry) {
+ this.uiData.highlightedItems = this.highlightedItems;
+ this.uiData.rects = this.generateRects();
+ this.uiData.displayIds = this.displayIds;
+ this.uiData.tree = this.generateTree();
+ }
+
+ this.notifyViewCallback(this.uiData);
+ }
+
+ private generateRects(): Rectangle[] {
+ const displayRects: Rectangle[] =
+ this.entry?.displays?.map((display: DisplayContent) => {
+ const rect = display.displayRect;
+ rect.label = `Display - ${display.title}`;
+ rect.stableId = display.stableId;
+ rect.displayId = display.id;
+ rect.isDisplay = true;
+ rect.cornerRadius = 0;
+ rect.isVirtual = false;
+ return rect;
+ }) ?? [];
+ this.displayIds = [];
+ const rects: Rectangle[] =
+ this.entry?.windowStates
+ ?.sort((a: any, b: any) => b.computedZ - a.computedZ)
+ .map((it: any) => {
+ const rect = it.rect;
+ rect.id = it.layerId;
+ rect.displayId = it.displayId;
+ rect.cornerRadius = 0;
+ if (!this.displayIds.includes(it.displayId)) {
+ this.displayIds.push(it.displayId);
+ }
+ return rect;
+ }) ?? [];
+ this.displayIds.sort();
+ return this.rectsToUiData(rects.concat(displayRects));
+ }
+
+ private updateSelectedTreeUiData() {
+ if (this.selectedHierarchyTree) {
+ this.uiData.propertiesTree = this.getTreeWithTransformedProperties(
+ this.selectedHierarchyTree
+ );
+ }
+ this.notifyViewCallback(this.uiData);
+ }
+
+ private generateTree() {
+ if (!this.entry) {
+ return null;
+ }
+
+ const generator = new TreeGenerator(this.entry, this.hierarchyFilter, this.pinnedIds)
+ .setIsOnlyVisibleView(this.hierarchyUserOptions['onlyVisible']?.enabled)
+ .setIsSimplifyNames(this.hierarchyUserOptions['simplifyNames']?.enabled)
+ .setIsFlatView(this.hierarchyUserOptions['flat']?.enabled)
+ .withUniqueNodeId();
+ let tree: HierarchyTreeNode | null;
+ if (!this.hierarchyUserOptions['showDiff']?.enabled) {
+ tree = generator.generateTree();
+ } else {
+ tree = generator
+ .compareWith(this.previousEntry)
+ .withModifiedCheck()
+ .generateFinalTreeWithDiff();
+ }
+ this.pinnedItems = generator.getPinnedItems();
+ this.uiData.pinnedItems = this.pinnedItems;
+ return tree;
+ }
+
+ private rectsToUiData(rects: any[]): Rectangle[] {
+ const uiRects: Rectangle[] = [];
+ const identityMatrix: RectMatrix = {
+ dsdx: 1,
+ dsdy: 0,
+ tx: 0,
+ dtdx: 0,
+ dtdy: 1,
+ ty: 0,
+ };
+ rects.forEach((rect: any) => {
+ const transform: RectTransform = {
+ matrix: identityMatrix,
+ };
+
+ const newRect: Rectangle = {
+ topLeft: {x: rect.left, y: rect.top},
+ bottomRight: {x: rect.right, y: rect.bottom},
+ label: rect.label,
+ transform,
+ isVisible: rect.ref?.isVisible ?? false,
+ isDisplay: rect.isDisplay ?? false,
+ ref: rect.ref,
+ id: rect.stableId ?? rect.ref.stableId,
+ displayId: rect.displayId ?? rect.ref.stackId,
+ isVirtual: rect.isVirtual ?? false,
+ isClickable: !rect.isDisplay,
+ cornerRadius: rect.cornerRadius,
+ };
+ uiRects.push(newRect);
+ });
+ return uiRects;
+ }
+
+ private updatePinnedIds(newId: string) {
+ if (this.pinnedIds.includes(newId)) {
+ this.pinnedIds = this.pinnedIds.filter((pinned) => pinned !== newId);
+ } else {
+ this.pinnedIds.push(newId);
+ }
+ }
+
+ private getTreeWithTransformedProperties(selectedTree: HierarchyTreeNode): PropertiesTreeNode {
+ if (!this.entry) {
+ return {};
+ }
+ const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
+ .setOnlyProtoDump(true)
+ .setIsShowDefaults(this.propertiesUserOptions['showDefaults']?.enabled)
+ .setIsShowDiff(this.propertiesUserOptions['showDiff']?.enabled)
+ .setTransformerOptions({skip: selectedTree.skip})
+ .setProperties(this.entry)
+ .setDiffProperties(this.previousEntry);
+ const transformedTree = transformer.transform();
+ return transformedTree;
+ }
+}
diff --git a/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts b/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts
new file mode 100644
index 0000000..fc87d09
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2022 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 ANYf KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
+import {MockStorage} from 'test/unit/mock_storage';
+import {TraceBuilder} from 'test/unit/trace_builder';
+import {UnitTestUtils} from 'test/unit/utils';
+import {WindowManagerState} from 'trace/flickerlib/common';
+import {RealTimestamp} from 'trace/timestamp';
+import {Trace} from 'trace/trace';
+import {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {VISIBLE_CHIP} from 'viewers/common/chip';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+describe('PresenterWindowManager', () => {
+ let trace: Trace<WindowManagerState>;
+ let position: TracePosition;
+ let presenter: Presenter;
+ let uiData: UiData;
+ let selectedTree: HierarchyTreeNode;
+
+ beforeAll(async () => {
+ trace = new TraceBuilder<WindowManagerState>()
+ .setEntries([await UnitTestUtils.getWindowManagerState()])
+ .build();
+
+ position = TracePosition.fromTraceEntry(trace.getEntry(0));
+
+ selectedTree = new HierarchyTreeBuilder()
+ .setName('ScreenDecorOverlayBottom')
+ .setStableId('WindowState 2088ac1 ScreenDecorOverlayBottom')
+ .setKind('WindowState')
+ .setSimplifyNames(true)
+ .setShortName('ScreenDecorOverlayBottom')
+ .setLayerId(61)
+ .setFilteredView(true)
+ .setIsVisible(true)
+ .setChips([VISIBLE_CHIP])
+ .build();
+ });
+
+ beforeEach(async () => {
+ presenter = createPresenter(trace);
+ });
+
+ it('is robust to empty trace', () => {
+ const emptyTrace = new TraceBuilder<WindowManagerState>().setEntries([]).build();
+ const presenter = createPresenter(emptyTrace);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.tree).toBeFalsy();
+
+ const positionWithoutTraceEntry = TracePosition.fromTimestamp(new RealTimestamp(0n));
+ presenter.onTracePositionUpdate(positionWithoutTraceEntry);
+ expect(uiData.hierarchyUserOptions).toBeTruthy();
+ expect(uiData.tree).toBeFalsy();
+ });
+
+ it('processes trace position update', () => {
+ presenter.onTracePositionUpdate(position);
+ const filteredUiDataRectLabels = uiData.rects
+ ?.filter((rect) => rect.isVisible !== undefined)
+ .map((rect) => rect.label);
+ const hierarchyOpts = uiData.hierarchyUserOptions
+ ? Object.keys(uiData.hierarchyUserOptions)
+ : null;
+ const propertyOpts = uiData.propertiesUserOptions
+ ? Object.keys(uiData.propertiesUserOptions)
+ : null;
+ expect(uiData.highlightedItems?.length).toEqual(0);
+ expect(filteredUiDataRectLabels?.length).toEqual(14);
+ expect(uiData.displayIds).toContain(0);
+ expect(hierarchyOpts).toBeTruthy();
+ expect(propertyOpts).toBeTruthy();
+
+ // does not check specific tree values as tree generation method may change
+ expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
+ });
+
+ it('creates input data for rects view', () => {
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.rects.length).toBeGreaterThan(0);
+ expect(uiData.rects[0].topLeft).toEqual({x: 0, y: 2326});
+ expect(uiData.rects[0].bottomRight).toEqual({x: 1080, y: 2400});
+ });
+
+ it('updates pinned items', () => {
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.pinnedItems).toEqual([]);
+
+ const pinnedItem = new HierarchyTreeBuilder()
+ .setName('FirstPinnedItem')
+ .setStableId('TestItem 4')
+ .setLayerId(4)
+ .build();
+
+ presenter.updatePinnedItems(pinnedItem);
+ expect(uiData.pinnedItems).toContain(pinnedItem);
+ });
+
+ it('updates highlighted items', () => {
+ expect(uiData.highlightedItems).toEqual([]);
+ const id = '4';
+ presenter.updateHighlightedItems(id);
+ expect(uiData.highlightedItems).toContain(id);
+ });
+
+ it('updates hierarchy tree', () => {
+ //change flat view to true
+ const userOptions: UserOptions = {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: true,
+ },
+ };
+
+ presenter.onTracePositionUpdate(position);
+ expect(uiData.tree?.children.length).toEqual(1);
+ presenter.updateHierarchyTree(userOptions);
+ expect(uiData.hierarchyUserOptions).toEqual(userOptions);
+ // nested children should now be on same level initial parent
+ expect(uiData.tree?.children.length).toEqual(72);
+ });
+
+ it('filters hierarchy tree', () => {
+ const userOptions: UserOptions = {
+ showDiff: {
+ name: 'Show diff',
+ enabled: false,
+ },
+ simplifyNames: {
+ name: 'Simplify names',
+ enabled: true,
+ },
+ onlyVisible: {
+ name: 'Only visible',
+ enabled: false,
+ },
+ flat: {
+ name: 'Flat',
+ enabled: true,
+ },
+ };
+ presenter.onTracePositionUpdate(position);
+ presenter.updateHierarchyTree(userOptions);
+ expect(uiData.tree?.children.length).toEqual(72);
+ presenter.filterHierarchyTree('ScreenDecor');
+ // All but two window states should be filtered out
+ expect(uiData.tree?.children.length).toEqual(2);
+ });
+
+ it('sets properties tree and associated ui data', () => {
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ // does not check specific tree values as tree transformation method may change
+ expect(uiData.propertiesTree).toBeTruthy();
+ });
+
+ it('updates properties tree', () => {
+ //change flat view to true
+ const userOptions: UserOptions = {
+ showDiff: {
+ name: 'Show diff',
+ enabled: true,
+ },
+ showDefaults: {
+ name: 'Show defaults',
+ enabled: true,
+ tooltip: `
+ If checked, shows the value of all properties.
+ Otherwise, hides all properties whose value is
+ the default for its data type.
+ `,
+ },
+ };
+
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+ expect(uiData.propertiesTree?.diffType).toBeFalsy();
+ presenter.updatePropertiesTree(userOptions);
+ expect(uiData.propertiesUserOptions).toEqual(userOptions);
+ //check that diff type added
+ expect(uiData.propertiesTree?.diffType).toBeTruthy();
+ });
+
+ it('filters properties tree', () => {
+ presenter.onTracePositionUpdate(position);
+ presenter.newPropertiesTree(selectedTree);
+
+ let nonTerminalChildren =
+ uiData.propertiesTree?.children?.filter(
+ (child: PropertiesTreeNode) => typeof child.propertyKey === 'string'
+ ) ?? [];
+
+ expect(nonTerminalChildren.length).toEqual(16);
+ presenter.filterPropertiesTree('visible');
+
+ nonTerminalChildren =
+ uiData.propertiesTree?.children?.filter(
+ (child: PropertiesTreeNode) => typeof child.propertyKey === 'string'
+ ) ?? [];
+ expect(nonTerminalChildren.length).toEqual(1);
+ });
+
+ const createPresenter = (trace: Trace<WindowManagerState>): Presenter => {
+ const traces = new Traces();
+ traces.setTrace(TraceType.WINDOW_MANAGER, trace);
+ return new Presenter(traces, new MockStorage(), (newData: UiData) => {
+ uiData = newData;
+ });
+ };
+});
diff --git a/tools/winscope/src/viewers/viewer_window_manager/ui_data.ts b/tools/winscope/src/viewers/viewer_window_manager/ui_data.ts
new file mode 100644
index 0000000..35bcaf7
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_window_manager/ui_data.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 {TraceType} from 'trace/trace_type';
+import {Rectangle} from 'viewers/common/rectangle';
+import {HierarchyTreeNode, PropertiesTreeNode} from 'viewers/common/ui_tree_utils';
+import {UserOptions} from 'viewers/common/user_options';
+
+export class UiData {
+ dependencies: TraceType[];
+ rects: Rectangle[] = [];
+ displayIds: number[] = [];
+ highlightedItems: string[] = [];
+ pinnedItems: HierarchyTreeNode[] = [];
+ hierarchyUserOptions: UserOptions = {};
+ propertiesUserOptions: UserOptions = {};
+ tree: HierarchyTreeNode | null = null;
+ propertiesTree: PropertiesTreeNode | null = null;
+
+ constructor(dependencies?: TraceType[]) {
+ this.dependencies = dependencies ?? [];
+ }
+}
diff --git a/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts
new file mode 100644
index 0000000..29726f8
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 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 {Traces} from 'trace/traces';
+import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
+import {ViewerEvents} from 'viewers/common/viewer_events';
+import {View, Viewer, ViewType} from 'viewers/viewer';
+import {Presenter} from './presenter';
+import {UiData} from './ui_data';
+
+class ViewerWindowManager implements Viewer {
+ constructor(traces: Traces, storage: Storage) {
+ this.htmlElement = document.createElement('viewer-window-manager');
+ this.presenter = new Presenter(traces, storage, (uiData: UiData) => {
+ (this.htmlElement as any).inputData = uiData;
+ });
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) =>
+ this.presenter.updatePinnedItems((event as CustomEvent).detail.pinnedItem)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HighlightedChange, (event) =>
+ this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) =>
+ this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.HierarchyFilterChange, (event) =>
+ this.presenter.filterHierarchyTree((event as CustomEvent).detail.filterString)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.PropertiesUserOptionsChange, (event) =>
+ this.presenter.updatePropertiesTree((event as CustomEvent).detail.userOptions)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.PropertiesFilterChange, (event) =>
+ this.presenter.filterPropertiesTree((event as CustomEvent).detail.filterString)
+ );
+ this.htmlElement.addEventListener(ViewerEvents.SelectedTreeChange, (event) =>
+ this.presenter.newPropertiesTree((event as CustomEvent).detail.selectedItem)
+ );
+ }
+
+ onTracePositionUpdate(position: TracePosition) {
+ this.presenter.onTracePositionUpdate(position);
+ }
+
+ getViews(): View[] {
+ return [
+ new View(
+ ViewType.TAB,
+ this.getDependencies(),
+ this.htmlElement,
+ 'Window Manager',
+ TraceType.WINDOW_MANAGER
+ ),
+ ];
+ }
+
+ getDependencies(): TraceType[] {
+ return ViewerWindowManager.DEPENDENCIES;
+ }
+
+ static readonly DEPENDENCIES: TraceType[] = [TraceType.WINDOW_MANAGER];
+ private htmlElement: HTMLElement;
+ private presenter: Presenter;
+}
+
+export {ViewerWindowManager};
diff --git a/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component.ts b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component.ts
new file mode 100644
index 0000000..d588083
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component.ts
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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 {Component, Input} from '@angular/core';
+import {TRACE_INFO} from 'app/trace_info';
+import {PersistentStore} from 'common/persistent_store';
+import {TraceType} from 'trace/trace_type';
+import {UiData} from './ui_data';
+
+@Component({
+ selector: 'viewer-window-manager',
+ template: `
+ <div class="card-grid">
+ <rects-view
+ class="rects-view"
+ title="Windows"
+ [rects]="inputData?.rects ?? []"
+ [displayIds]="inputData?.displayIds ?? []"
+ [highlightedItems]="inputData?.highlightedItems ?? []"></rects-view>
+ <mat-divider [vertical]="true"></mat-divider>
+ <hierarchy-view
+ class="hierarchy-view"
+ [tree]="inputData?.tree ?? null"
+ [dependencies]="inputData?.dependencies ?? []"
+ [highlightedItems]="inputData?.highlightedItems ?? []"
+ [pinnedItems]="inputData?.pinnedItems ?? []"
+ [store]="store"
+ [userOptions]="inputData?.hierarchyUserOptions ?? {}"></hierarchy-view>
+ <mat-divider [vertical]="true"></mat-divider>
+ <properties-view
+ class="properties-view"
+ [userOptions]="inputData?.propertiesUserOptions ?? {}"
+ [propertiesTree]="inputData?.propertiesTree ?? {}"
+ [isProtoDump]="true"></properties-view>
+ </div>
+ `,
+ styles: [
+ `
+ .rects-view,
+ .hierarchy-view,
+ .properties-view {
+ flex: 1;
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ }
+ `,
+ ],
+})
+export class ViewerWindowManagerComponent {
+ @Input() inputData?: UiData;
+ @Input() store: PersistentStore = new PersistentStore();
+ @Input() active = false;
+ TRACE_INFO = TRACE_INFO;
+ TraceType = TraceType;
+}
diff --git a/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component_test.ts b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component_test.ts
new file mode 100644
index 0000000..52b9cb7
--- /dev/null
+++ b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component_test.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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 {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
+import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '@angular/core/testing';
+import {MatDividerModule} from '@angular/material/divider';
+import {MatIconModule} from '@angular/material/icon';
+import {HierarchyComponent} from 'viewers/components/hierarchy_component';
+import {PropertiesComponent} from 'viewers/components/properties_component';
+import {RectsComponent} from 'viewers/components/rects/rects_component';
+import {ViewerWindowManagerComponent} from './viewer_window_manager_component';
+
+describe('ViewerWindowManagerComponent', () => {
+ let fixture: ComponentFixture<ViewerWindowManagerComponent>;
+ let component: ViewerWindowManagerComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
+ imports: [MatIconModule, MatDividerModule],
+ declarations: [
+ ViewerWindowManagerComponent,
+ HierarchyComponent,
+ PropertiesComponent,
+ RectsComponent,
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ViewerWindowManagerComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it('can be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('creates rects view', () => {
+ const rectsView = htmlElement.querySelector('.rects-view');
+ expect(rectsView).toBeTruthy();
+ });
+
+ it('creates hierarchy view', () => {
+ const hierarchyView = htmlElement.querySelector('.hierarchy-view');
+ expect(hierarchyView).toBeTruthy();
+ });
+
+ it('creates properties view', () => {
+ const propertiesView = htmlElement.querySelector('.properties-view');
+ expect(propertiesView).toBeTruthy();
+ });
+});
diff --git a/tools/winscope/tests/samples/wl_trace.pb b/tools/winscope/tests/samples/wl_trace.pb
deleted file mode 100644
index 7e1f007..0000000
--- a/tools/winscope/tests/samples/wl_trace.pb
+++ /dev/null
Binary files differ
diff --git a/tools/winscope/trace.sh b/tools/winscope/trace.sh
deleted file mode 100755
index ff01748..0000000
--- a/tools/winscope/trace.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 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.
-#
-
-WINSCOPE_URL='http://go/winscope/'
-
-set -e
-
-help=
-
-for arg in "$@"; do
- case $arg in
- -h|--help) help=1;;
- --);;
- -*) echo "Unknown option: $arg"; help=1;;
- *) outfile="$arg";;
- esac
-done
-
-WINSCOPE_EXT=.winscope
-outfileTrans=${outfile}_transactiontrace$WINSCOPE_EXT
-outfileSurf=${outfile}_layerstrace$WINSCOPE_EXT
-
-outfileTrans_abs="$(cd "$(dirname "$outfileTrans")"; pwd)/$(basename "$outfileTrans")"
-outfileSurf_abs="$(cd "$(dirname "$outfileSurf")"; pwd)/$(basename "$outfileSurf")"
-
-if [ "$help" != "" ]; then
- echo "usage: $0 [-h | --help] [OUTFILE]"
- echo
- echo "Records Transaction traces (default transactiontrace$WINSCOPE_EXT)."
- echo "Records Surface traces (default layerstrace$WINSCOPE_EXT)."
- echo "To view the traces, use $WINSCOPE_URL."
- exit 1
-fi
-
-function log_error() {
- echo "FAILED"
-}
-trap log_error ERR
-
-function start_tracing() {
- echo -n "Starting transaction and surface recording..."
- echo
- adb shell su root service call SurfaceFlinger 1020 i32 1 >/dev/null
- adb shell su root service call SurfaceFlinger 1025 i32 1 >/dev/null
- echo "DONE"
- trap stop_tracing EXIT
-}
-
-function stop_tracing() {
- echo -n "Stopping transaction and surface recording..."
- echo
- adb shell su root service call SurfaceFlinger 1020 i32 0 >/dev/null
- adb shell su root service call SurfaceFlinger 1025 i32 0 >/dev/null
- echo "DONE"
- trap - EXIT
-}
-
-which adb >/dev/null 2>/dev/null || { echo "ERROR: ADB not found."; exit 1; }
-adb get-state 2>/dev/null | grep -q device || { echo "ERROR: No device connected or device is unauthorized."; exit 1; }
-
-start_tracing
-read -p "Press ENTER to stop recording" -s x
-echo
-stop_tracing
-adb exec-out su root cat /data/misc/wmtrace/transaction_trace$WINSCOPE_EXT >"$outfileTrans"
-adb exec-out su root cat /data/misc/wmtrace/layers_trace$WINSCOPE_EXT >"$outfileSurf"
-
-echo
-echo "To view the trace, go to $WINSCOPE_URL, and open"
-echo "${outfileTrans_abs}"
-echo "${outfileSurf_abs}"
diff --git a/tools/winscope/tsconfig.json b/tools/winscope/tsconfig.json
index 486e68b..c53b32e 100644
--- a/tools/winscope/tsconfig.json
+++ b/tools/winscope/tsconfig.json
@@ -1,22 +1,37 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
+ "compileOnSave": false,
"compilerOptions": {
"baseUrl": "./src",
- "outDir": "./dist/",
- "noImplicitAny": false,
- "module": "es6",
- "target": "es5",
- "allowJs": false,
- "resolveJsonModule": true,
- "moduleResolution": "node",
+ "outDir": "./dist/out-tsc",
+ "allowJs": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
+ "sourceMap": true,
+ "declaration": false,
+ "downlevelIteration": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "target": "es2020",
+ "module": "es2020",
"lib": [
- "es2019",
- "dom",
+ "es2020",
+ "dom"
],
- "paths": {
- "@/*": [
- "*"
- ],
- }
+ "resolveJsonModule": true,
+ },
+ "include": ["src"],
+ "exclude": ["src/test/e2e"],
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false,
+ "strictInjectionParameters": true,
+ "strictInputAccessModifiers": true,
+ "strictTemplates": true
}
-}
\ No newline at end of file
+}
diff --git a/tools/winscope/vue.config.js b/tools/winscope/vue.config.js
deleted file mode 100644
index ab7013e..0000000
--- a/tools/winscope/vue.config.js
+++ /dev/null
@@ -1,5 +0,0 @@
-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
index 48d950a..8a3a00c 100644
--- a/tools/winscope/webpack.config.common.js
+++ b/tools/winscope/webpack.config.common.js
@@ -1,11 +1,11 @@
/*
- * Copyright 2020, The Android Open Source Project
+ * Copyright (C) 2022 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
+ * 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,
@@ -13,87 +13,36 @@
* 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 TerserPlugin = require('terser-webpack-plugin');
-const { VueLoaderPlugin } = require("vue-loader")
-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',
- },
+module.exports = {
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, '../../..'),
- ],
+ extensions: ['.ts', '.js', '.css'],
+ modules: ['node_modules', 'src', 'kotlin_build', path.resolve(__dirname, '../../..')],
},
+
resolveLoader: {
- modules: [
- 'node_modules',
- path.resolve(__dirname, 'loaders'),
- ],
+ modules: ['node_modules', path.resolve(__dirname, 'loaders')],
},
+
module: {
rules: [
{
- test: /\.vue$/,
- loader: 'vue-loader',
- include: path.resolve(__dirname, './src'),
- exclude: /node_modules/,
+ test: /\.ts$/,
+ use: ['ts-loader', 'angular2-template-loader'],
},
{
- 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: /\.html$/,
+ use: ['html-loader'],
},
{
test: /\.css$/,
- use: [
- 'vue-style-loader',
- {loader: 'css-loader', options: {sourceMap: isDev}},
- ],
- include: path.resolve(__dirname, './src'),
- exclude: /node_modules/,
+ use: ['style-loader', 'css-loader'],
+ },
+ {
+ test: /\.s[ac]ss$/i,
+ use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.proto$/,
@@ -105,45 +54,16 @@
],
},
},
- {
- 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(HtmlWebpackPlugin),
- new KotlinWebpackPlugin({
- src: [
- path.join(__dirname, '../../../platform_testing/libraries/flicker/' +
- 'src/com/android/server/wm/traces/common/'),
- ],
- output: 'kotlin_build',
- moduleName: 'flicker',
- librariesAutoLookup: true,
- sourceMaps: true,
- sourceMapEmbedSources: 'always',
- verbose: true,
- optimize: true,
- }),
- ],
-};
-module.exports = webpackConfig;
+ optimization: {
+ minimizer: [
+ new TerserPlugin({
+ terserOptions: {
+ keep_fnames: true,
+ },
+ }),
+ ],
+ },
+};
diff --git a/tools/winscope/webpack.config.dev.js b/tools/winscope/webpack.config.dev.js
index 31d79ca..5857a8e 100644
--- a/tools/winscope/webpack.config.dev.js
+++ b/tools/winscope/webpack.config.dev.js
@@ -1,11 +1,11 @@
/*
- * Copyright 2020, The Android Open Source Project
+ * Copyright (C) 2022 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
+ * 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,
@@ -13,61 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+const {merge} = require('webpack-merge');
+const configCommon = require('./webpack.config.common');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
-'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, {
+const configDev = {
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'
+ entry: {
+ polyfills: './src/polyfills.ts',
+ styles: ['./src/material-theme.scss', './src/styles.css'],
+ app: './src/main_dev.ts',
},
- 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'
- },
- ]
- },
+ devtool: 'source-map',
plugins: [
- new webpack.EnvironmentPlugin(environment),
- new webpack.HotModuleReplacementPlugin(),
- new FriendlyErrorsPlugin()
+ new HtmlWebpackPlugin({
+ template: 'src/index.html',
+ inject: 'body',
+ inlineSource: '.(css|js)$',
+ }),
],
- 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
+module.exports = merge(configCommon, configDev);
diff --git a/tools/winscope/webpack.config.js b/tools/winscope/webpack.config.js
deleted file mode 100644
index ac6ab23..0000000
--- a/tools/winscope/webpack.config.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 environment = (process.env.NODE_ENV || 'development').trim();
-
-if (environment === 'development') {
- module.exports = require('./webpack.config.dev');
-} else {
- module.exports = require('./webpack.config.prod');
-}
diff --git a/tools/winscope/webpack.config.prod.js b/tools/winscope/webpack.config.prod.js
index 33f7a8d..6128a9a 100644
--- a/tools/winscope/webpack.config.prod.js
+++ b/tools/winscope/webpack.config.prod.js
@@ -1,11 +1,11 @@
/*
- * Copyright 2020, The Android Open Source Project
+ * Copyright (C) 2022 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
+ * 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,
@@ -13,38 +13,27 @@
* 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 configCommon = require('./webpack.config.common');
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 HtmlWebpackPlugin = require('html-webpack-plugin');
+const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
-
-const webpackConfig = merge(commonConfig, {
+const configProd = {
mode: 'production',
+ entry: {
+ polyfills: './src/polyfills.ts',
+ styles: ['./src/material-theme.scss', './src/styles.css'],
+ app: './src/main_prod.ts',
+ },
output: {
- path: path.resolve(__dirname, 'dist'),
+ path: path.resolve(__dirname, 'dist/prod'),
publicPath: '/',
- filename: 'js/[hash].js',
- chunkFilename: 'js/[id].[hash].chunk.js',
+ filename: 'js/[name].[hash].js',
+ chunkFilename: 'js/[name].[id].[hash].chunk.js',
},
optimization: {
runtimeChunk: 'single',
- minimizer: [
- new OptimizeCSSAssetsPlugin({
- cssProcessorPluginOptions: {
- preset: ['default', {discardComments: {removeAll: true}}],
- },
- }),
- ],
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
@@ -53,8 +42,7 @@
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
- const packageName = module.context
- .match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
+ const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName.replace('@', '')}`;
},
},
@@ -68,30 +56,13 @@
},
},
plugins: [
- new webpack.EnvironmentPlugin(environment),
- new MiniCSSExtractPlugin({
- filename: 'css/[name].[hash].css',
- chunkFilename: 'css/[id].[hash].css',
+ new HtmlWebpackPlugin({
+ template: 'src/index.html',
+ inject: 'body',
+ inlineSource: '.(css|js)$',
}),
- new CompressionPlugin({
- filename: '[path].gz[query]',
- algorithm: 'gzip',
- test: new RegExp('\\.(js|css)$'),
- threshold: 10240,
- minRatio: 0.8,
- }),
- new webpack.HashedModuleIdsPlugin(),
+ new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
],
-});
+};
-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;
+module.exports = merge(configCommon, configProd);
diff --git a/tools/winscope/webpack.config.unit_test.js b/tools/winscope/webpack.config.unit_test.js
new file mode 100644
index 0000000..15b4ed4
--- /dev/null
+++ b/tools/winscope/webpack.config.unit_test.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 path = require('path');
+const glob = require('glob');
+
+const config = require('./webpack.config.common');
+
+config['mode'] = 'development';
+
+const allTestFiles = [...glob.sync('./src/**/*_test.js'), ...glob.sync('./src/**/*_test.ts')];
+const unitTestFiles = allTestFiles
+ .filter((file) => !file.match('.*_component_test\\.(js|ts)$'))
+ .filter((file) => !file.match('.*e2e.*'));
+config['entry'] = {
+ tests: unitTestFiles,
+};
+
+config['output'] = {
+ path: path.resolve(__dirname, 'dist/unit_test'),
+ filename: 'bundle.js',
+};
+
+config['target'] = 'node';
+config['node'] = {
+ __dirname: false,
+};
+
+module.exports = config;
diff --git a/tools/winscope/webpack.spec.config.js b/tools/winscope/webpack.spec.config.js
deleted file mode 100644
index 080da80..0000000
--- a/tools/winscope/webpack.spec.config.js
+++ /dev/null
@@ -1,117 +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.
- */
-
-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|winscope)/,
- 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/traces/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
deleted file mode 100644
index a25b413..0000000
--- a/tools/winscope/yarn.lock
+++ /dev/null
@@ -1,8079 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@babel/code-frame@7.12.11":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
- integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
- dependencies:
- "@babel/highlight" "^7.10.4"
-
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
- integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==
- dependencies:
- "@babel/highlight" "^7.14.5"
-
-"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08"
- integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==
-
-"@babel/core@^7.14.6":
- version "7.14.6"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab"
- integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==
- dependencies:
- "@babel/code-frame" "^7.14.5"
- "@babel/generator" "^7.14.5"
- "@babel/helper-compilation-targets" "^7.14.5"
- "@babel/helper-module-transforms" "^7.14.5"
- "@babel/helpers" "^7.14.6"
- "@babel/parser" "^7.14.6"
- "@babel/template" "^7.14.5"
- "@babel/traverse" "^7.14.5"
- "@babel/types" "^7.14.5"
- convert-source-map "^1.7.0"
- debug "^4.1.0"
- gensync "^1.0.0-beta.2"
- json5 "^2.1.2"
- semver "^6.3.0"
- source-map "^0.5.0"
-
-"@babel/generator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785"
- integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==
- dependencies:
- "@babel/types" "^7.14.5"
- jsesc "^2.5.1"
- source-map "^0.5.0"
-
-"@babel/helper-annotate-as-pure@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61"
- integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191"
- integrity sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w==
- dependencies:
- "@babel/helper-explode-assignable-expression" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf"
- integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==
- dependencies:
- "@babel/compat-data" "^7.14.5"
- "@babel/helper-validator-option" "^7.14.5"
- browserslist "^4.16.6"
- semver "^6.3.0"
-
-"@babel/helper-create-class-features-plugin@^7.14.5":
- version "7.14.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542"
- integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.14.5"
- "@babel/helper-function-name" "^7.14.5"
- "@babel/helper-member-expression-to-functions" "^7.14.5"
- "@babel/helper-optimise-call-expression" "^7.14.5"
- "@babel/helper-replace-supers" "^7.14.5"
- "@babel/helper-split-export-declaration" "^7.14.5"
-
-"@babel/helper-create-regexp-features-plugin@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4"
- integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.14.5"
- regexpu-core "^4.7.1"
-
-"@babel/helper-define-polyfill-provider@^0.2.2":
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6"
- integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew==
- dependencies:
- "@babel/helper-compilation-targets" "^7.13.0"
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-plugin-utils" "^7.13.0"
- "@babel/traverse" "^7.13.0"
- debug "^4.1.1"
- lodash.debounce "^4.0.8"
- resolve "^1.14.2"
- semver "^6.1.2"
-
-"@babel/helper-explode-assignable-expression@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645"
- integrity sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-function-name@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4"
- integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==
- dependencies:
- "@babel/helper-get-function-arity" "^7.14.5"
- "@babel/template" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/helper-get-function-arity@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815"
- integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-hoist-variables@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d"
- integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-member-expression-to-functions@^7.14.5":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970"
- integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
- integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-module-transforms@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e"
- integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==
- dependencies:
- "@babel/helper-module-imports" "^7.14.5"
- "@babel/helper-replace-supers" "^7.14.5"
- "@babel/helper-simple-access" "^7.14.5"
- "@babel/helper-split-export-declaration" "^7.14.5"
- "@babel/helper-validator-identifier" "^7.14.5"
- "@babel/template" "^7.14.5"
- "@babel/traverse" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/helper-optimise-call-expression@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c"
- integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
- integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==
-
-"@babel/helper-remap-async-to-generator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz#51439c913612958f54a987a4ffc9ee587a2045d6"
- integrity sha512-rLQKdQU+HYlxBwQIj8dk4/0ENOUEhA/Z0l4hN8BexpvmSMN9oA9EagjnhnDpNsRdWCfjwa4mn/HyBXO9yhQP6A==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.14.5"
- "@babel/helper-wrap-function" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/helper-replace-supers@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94"
- integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==
- dependencies:
- "@babel/helper-member-expression-to-functions" "^7.14.5"
- "@babel/helper-optimise-call-expression" "^7.14.5"
- "@babel/traverse" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/helper-simple-access@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4"
- integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-skip-transparent-expression-wrappers@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4"
- integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-split-export-declaration@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a"
- integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==
- dependencies:
- "@babel/types" "^7.14.5"
-
-"@babel/helper-validator-identifier@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8"
- integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==
-
-"@babel/helper-validator-option@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
- integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
-
-"@babel/helper-wrap-function@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz#5919d115bf0fe328b8a5d63bcb610f51601f2bff"
- integrity sha512-YEdjTCq+LNuNS1WfxsDCNpgXkJaIyqco6DAelTUjT4f2KIWC1nBcaCaSdHTBqQVLnTBexBcVcFhLSU1KnYuePQ==
- dependencies:
- "@babel/helper-function-name" "^7.14.5"
- "@babel/template" "^7.14.5"
- "@babel/traverse" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/helpers@^7.14.6":
- version "7.14.6"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635"
- integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==
- dependencies:
- "@babel/template" "^7.14.5"
- "@babel/traverse" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
- integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
- dependencies:
- "@babel/helper-validator-identifier" "^7.14.5"
- chalk "^2.0.0"
- js-tokens "^4.0.0"
-
-"@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595"
- integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA==
-
-"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e"
- integrity sha512-ZoJS2XCKPBfTmL122iP6NM9dOg+d4lc9fFk3zxc8iDjvt8Pk4+TlsHSKhIPf6X+L5ORCdBzqMZDjL/WHj7WknQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5"
- "@babel/plugin-proposal-optional-chaining" "^7.14.5"
-
-"@babel/plugin-proposal-async-generator-functions@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz#784a48c3d8ed073f65adcf30b57bcbf6c8119ace"
- integrity sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-remap-async-to-generator" "^7.14.5"
- "@babel/plugin-syntax-async-generators" "^7.8.4"
-
-"@babel/plugin-proposal-class-properties@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e"
- integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-proposal-class-static-block@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.5.tgz#158e9e10d449c3849ef3ecde94a03d9f1841b681"
- integrity sha512-KBAH5ksEnYHCegqseI5N9skTdxgJdmDoAOc0uXa+4QMYKeZD0w5IARh4FMlTNtaHhbB8v+KzMdTgxMMzsIy6Yg==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-class-static-block" "^7.14.5"
-
-"@babel/plugin-proposal-dynamic-import@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz#0c6617df461c0c1f8fff3b47cd59772360101d2c"
- integrity sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-dynamic-import" "^7.8.3"
-
-"@babel/plugin-proposal-export-namespace-from@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz#dbad244310ce6ccd083072167d8cea83a52faf76"
- integrity sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
-
-"@babel/plugin-proposal-json-strings@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz#38de60db362e83a3d8c944ac858ddf9f0c2239eb"
- integrity sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-json-strings" "^7.8.3"
-
-"@babel/plugin-proposal-logical-assignment-operators@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz#6e6229c2a99b02ab2915f82571e0cc646a40c738"
- integrity sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
-
-"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6"
- integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
-
-"@babel/plugin-proposal-numeric-separator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz#83631bf33d9a51df184c2102a069ac0c58c05f18"
- integrity sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-numeric-separator" "^7.10.4"
-
-"@babel/plugin-proposal-object-rest-spread@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363"
- integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g==
- dependencies:
- "@babel/compat-data" "^7.14.7"
- "@babel/helper-compilation-targets" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
- "@babel/plugin-transform-parameters" "^7.14.5"
-
-"@babel/plugin-proposal-optional-catch-binding@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c"
- integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-
-"@babel/plugin-proposal-optional-chaining@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603"
- integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5"
- "@babel/plugin-syntax-optional-chaining" "^7.8.3"
-
-"@babel/plugin-proposal-private-methods@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz#37446495996b2945f30f5be5b60d5e2aa4f5792d"
- integrity sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-proposal-private-property-in-object@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.5.tgz#9f65a4d0493a940b4c01f8aa9d3f1894a587f636"
- integrity sha512-62EyfyA3WA0mZiF2e2IV9mc9Ghwxcg8YTu8BS4Wss4Y3PY725OmS9M0qLORbJwLqFtGh+jiE4wAmocK2CTUK2Q==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.14.5"
- "@babel/helper-create-class-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
-
-"@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8"
- integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-async-generators@^7.8.4":
- version "7.8.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
- integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-class-properties@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
- integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-syntax-class-static-block@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
- integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-dynamic-import@^7.8.3":
- 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.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
- integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
- 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.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
- integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-numeric-separator@^7.10.4":
- 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.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
- integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
- integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-optional-chaining@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
- integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-private-property-in-object@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
- integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-top-level-await@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
- integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-arrow-functions@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a"
- integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-async-to-generator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz#72c789084d8f2094acb945633943ef8443d39e67"
- integrity sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==
- dependencies:
- "@babel/helper-module-imports" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-remap-async-to-generator" "^7.14.5"
-
-"@babel/plugin-transform-block-scoped-functions@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4"
- integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-block-scoping@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939"
- integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-classes@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf"
- integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.14.5"
- "@babel/helper-function-name" "^7.14.5"
- "@babel/helper-optimise-call-expression" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-replace-supers" "^7.14.5"
- "@babel/helper-split-export-declaration" "^7.14.5"
- globals "^11.1.0"
-
-"@babel/plugin-transform-computed-properties@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f"
- integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-destructuring@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576"
- integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a"
- integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-duplicate-keys@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz#365a4844881bdf1501e3a9f0270e7f0f91177954"
- integrity sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-exponentiation-operator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493"
- integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA==
- dependencies:
- "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-for-of@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb"
- integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-function-name@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2"
- integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ==
- dependencies:
- "@babel/helper-function-name" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-literals@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78"
- integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-member-expression-literals@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7"
- integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-modules-amd@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz#4fd9ce7e3411cb8b83848480b7041d83004858f7"
- integrity sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g==
- dependencies:
- "@babel/helper-module-transforms" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-commonjs@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97"
- integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A==
- dependencies:
- "@babel/helper-module-transforms" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-simple-access" "^7.14.5"
- babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-systemjs@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.14.5.tgz#c75342ef8b30dcde4295d3401aae24e65638ed29"
- integrity sha512-mNMQdvBEE5DcMQaL5LbzXFMANrQjd2W7FPzg34Y4yEz7dBgdaC+9B84dSO+/1Wba98zoDbInctCDo4JGxz1VYA==
- dependencies:
- "@babel/helper-hoist-variables" "^7.14.5"
- "@babel/helper-module-transforms" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-validator-identifier" "^7.14.5"
- babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-umd@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz#fb662dfee697cce274a7cda525190a79096aa6e0"
- integrity sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA==
- dependencies:
- "@babel/helper-module-transforms" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-named-capturing-groups-regex@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz#60c06892acf9df231e256c24464bfecb0908fd4e"
- integrity sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.14.5"
-
-"@babel/plugin-transform-new-target@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz#31bdae8b925dc84076ebfcd2a9940143aed7dbf8"
- integrity sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-object-super@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45"
- integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-replace-supers" "^7.14.5"
-
-"@babel/plugin-transform-parameters@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3"
- integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-property-literals@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34"
- integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-regenerator@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f"
- integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg==
- dependencies:
- regenerator-transform "^0.14.2"
-
-"@babel/plugin-transform-reserved-words@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz#c44589b661cfdbef8d4300dcc7469dffa92f8304"
- integrity sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-shorthand-properties@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58"
- integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-spread@^7.14.6":
- version "7.14.6"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144"
- integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5"
-
-"@babel/plugin-transform-sticky-regex@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9"
- integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-template-literals@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93"
- integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-typeof-symbol@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz#39af2739e989a2bd291bf6b53f16981423d457d4"
- integrity sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-unicode-escapes@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b"
- integrity sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-unicode-regex@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e"
- integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/polyfill@^7.12.1":
- version "7.12.1"
- resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.12.1.tgz#1f2d6371d1261bbd961f3c5d5909150e12d0bd96"
- integrity sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==
- dependencies:
- core-js "^2.6.5"
- regenerator-runtime "^0.13.4"
-
-"@babel/preset-env@^7.14.7":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.7.tgz#5c70b22d4c2d893b03d8c886a5c17422502b932a"
- integrity sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==
- dependencies:
- "@babel/compat-data" "^7.14.7"
- "@babel/helper-compilation-targets" "^7.14.5"
- "@babel/helper-plugin-utils" "^7.14.5"
- "@babel/helper-validator-option" "^7.14.5"
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.14.5"
- "@babel/plugin-proposal-async-generator-functions" "^7.14.7"
- "@babel/plugin-proposal-class-properties" "^7.14.5"
- "@babel/plugin-proposal-class-static-block" "^7.14.5"
- "@babel/plugin-proposal-dynamic-import" "^7.14.5"
- "@babel/plugin-proposal-export-namespace-from" "^7.14.5"
- "@babel/plugin-proposal-json-strings" "^7.14.5"
- "@babel/plugin-proposal-logical-assignment-operators" "^7.14.5"
- "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5"
- "@babel/plugin-proposal-numeric-separator" "^7.14.5"
- "@babel/plugin-proposal-object-rest-spread" "^7.14.7"
- "@babel/plugin-proposal-optional-catch-binding" "^7.14.5"
- "@babel/plugin-proposal-optional-chaining" "^7.14.5"
- "@babel/plugin-proposal-private-methods" "^7.14.5"
- "@babel/plugin-proposal-private-property-in-object" "^7.14.5"
- "@babel/plugin-proposal-unicode-property-regex" "^7.14.5"
- "@babel/plugin-syntax-async-generators" "^7.8.4"
- "@babel/plugin-syntax-class-properties" "^7.12.13"
- "@babel/plugin-syntax-class-static-block" "^7.14.5"
- "@babel/plugin-syntax-dynamic-import" "^7.8.3"
- "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
- "@babel/plugin-syntax-json-strings" "^7.8.3"
- "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
- "@babel/plugin-syntax-numeric-separator" "^7.10.4"
- "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
- "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
- "@babel/plugin-syntax-optional-chaining" "^7.8.3"
- "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
- "@babel/plugin-syntax-top-level-await" "^7.14.5"
- "@babel/plugin-transform-arrow-functions" "^7.14.5"
- "@babel/plugin-transform-async-to-generator" "^7.14.5"
- "@babel/plugin-transform-block-scoped-functions" "^7.14.5"
- "@babel/plugin-transform-block-scoping" "^7.14.5"
- "@babel/plugin-transform-classes" "^7.14.5"
- "@babel/plugin-transform-computed-properties" "^7.14.5"
- "@babel/plugin-transform-destructuring" "^7.14.7"
- "@babel/plugin-transform-dotall-regex" "^7.14.5"
- "@babel/plugin-transform-duplicate-keys" "^7.14.5"
- "@babel/plugin-transform-exponentiation-operator" "^7.14.5"
- "@babel/plugin-transform-for-of" "^7.14.5"
- "@babel/plugin-transform-function-name" "^7.14.5"
- "@babel/plugin-transform-literals" "^7.14.5"
- "@babel/plugin-transform-member-expression-literals" "^7.14.5"
- "@babel/plugin-transform-modules-amd" "^7.14.5"
- "@babel/plugin-transform-modules-commonjs" "^7.14.5"
- "@babel/plugin-transform-modules-systemjs" "^7.14.5"
- "@babel/plugin-transform-modules-umd" "^7.14.5"
- "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.7"
- "@babel/plugin-transform-new-target" "^7.14.5"
- "@babel/plugin-transform-object-super" "^7.14.5"
- "@babel/plugin-transform-parameters" "^7.14.5"
- "@babel/plugin-transform-property-literals" "^7.14.5"
- "@babel/plugin-transform-regenerator" "^7.14.5"
- "@babel/plugin-transform-reserved-words" "^7.14.5"
- "@babel/plugin-transform-shorthand-properties" "^7.14.5"
- "@babel/plugin-transform-spread" "^7.14.6"
- "@babel/plugin-transform-sticky-regex" "^7.14.5"
- "@babel/plugin-transform-template-literals" "^7.14.5"
- "@babel/plugin-transform-typeof-symbol" "^7.14.5"
- "@babel/plugin-transform-unicode-escapes" "^7.14.5"
- "@babel/plugin-transform-unicode-regex" "^7.14.5"
- "@babel/preset-modules" "^0.1.4"
- "@babel/types" "^7.14.5"
- babel-plugin-polyfill-corejs2 "^0.2.2"
- babel-plugin-polyfill-corejs3 "^0.2.2"
- babel-plugin-polyfill-regenerator "^0.2.2"
- core-js-compat "^3.15.0"
- semver "^6.3.0"
-
-"@babel/preset-modules@^0.1.4":
- 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.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233"
- integrity sha512-TjJpGz/aDjFGWsItRBQMOFTrmTI9tr79CHOK+KIvLeCkbxuOAk2M5QHjvruIMGoo9OuccMh5euplPzc5FjAKGg==
- dependencies:
- clone-deep "^4.0.1"
- find-cache-dir "^2.0.0"
- make-dir "^2.1.0"
- pirates "^4.0.0"
- source-map-support "^0.5.16"
-
-"@babel/runtime-corejs3@^7.10.2":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz#0ef292bbce40ca00f874c9724ef175a12476465c"
- integrity sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==
- dependencies:
- core-js-pure "^3.15.0"
- regenerator-runtime "^0.13.4"
-
-"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
- version "7.14.6"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
- integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
- dependencies:
- regenerator-runtime "^0.13.4"
-
-"@babel/template@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
- integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==
- dependencies:
- "@babel/code-frame" "^7.14.5"
- "@babel/parser" "^7.14.5"
- "@babel/types" "^7.14.5"
-
-"@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5":
- version "7.14.7"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753"
- integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ==
- dependencies:
- "@babel/code-frame" "^7.14.5"
- "@babel/generator" "^7.14.5"
- "@babel/helper-function-name" "^7.14.5"
- "@babel/helper-hoist-variables" "^7.14.5"
- "@babel/helper-split-export-declaration" "^7.14.5"
- "@babel/parser" "^7.14.7"
- "@babel/types" "^7.14.5"
- debug "^4.1.0"
- globals "^11.1.0"
-
-"@babel/types@^7.14.5", "@babel/types@^7.4.4":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff"
- integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==
- dependencies:
- "@babel/helper-validator-identifier" "^7.14.5"
- to-fast-properties "^2.0.0"
-
-"@discoveryjs/json-ext@^0.5.0":
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
- integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==
-
-"@eslint/eslintrc@^0.4.2":
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179"
- integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==
- dependencies:
- ajv "^6.12.4"
- debug "^4.1.1"
- espree "^7.3.0"
- globals "^13.9.0"
- ignore "^4.0.6"
- import-fresh "^3.2.1"
- js-yaml "^3.13.1"
- minimatch "^3.0.4"
- strip-json-comments "^3.1.1"
-
-"@humanwhocodes/config-array@^0.5.0":
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
- integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
- dependencies:
- "@humanwhocodes/object-schema" "^1.2.0"
- debug "^4.1.1"
- minimatch "^3.0.4"
-
-"@humanwhocodes/object-schema@^1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
- integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
-
-"@jest/types@^26.6.2":
- version "26.6.2"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
- integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
- dependencies:
- "@types/istanbul-lib-coverage" "^2.0.0"
- "@types/istanbul-reports" "^3.0.0"
- "@types/node" "*"
- "@types/yargs" "^15.0.0"
- chalk "^4.0.0"
-
-"@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"
-
-"@nodelib/fs.scandir@2.1.5":
- version "2.1.5"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
- integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
- dependencies:
- "@nodelib/fs.stat" "2.0.5"
- run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
- integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
-
-"@nodelib/fs.walk@^1.2.3":
- version "1.2.8"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
- integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
- dependencies:
- "@nodelib/fs.scandir" "2.1.5"
- fastq "^1.6.0"
-
-"@npmcli/move-file@^1.0.1":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674"
- integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==
- dependencies:
- mkdirp "^1.0.4"
- rimraf "^3.0.2"
-
-"@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"
- integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78=
-
-"@protobufjs/base64@^1.1.2":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
- integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
-
-"@protobufjs/codegen@^2.0.4":
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
- integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
-
-"@protobufjs/eventemitter@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
- integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A=
-
-"@protobufjs/fetch@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
- integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=
- dependencies:
- "@protobufjs/aspromise" "^1.1.1"
- "@protobufjs/inquire" "^1.1.0"
-
-"@protobufjs/float@^1.0.2":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
- integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=
-
-"@protobufjs/inquire@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
- integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=
-
-"@protobufjs/path@^1.1.2":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
- integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=
-
-"@protobufjs/pool@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
- integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=
-
-"@protobufjs/utf8@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
- integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
-
-"@testing-library/dom@^7.26.6":
- version "7.31.2"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a"
- integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==
- dependencies:
- "@babel/code-frame" "^7.10.4"
- "@babel/runtime" "^7.12.5"
- "@types/aria-query" "^4.2.0"
- aria-query "^4.2.2"
- chalk "^4.1.0"
- dom-accessibility-api "^0.5.6"
- lz-string "^1.4.4"
- pretty-format "^26.6.2"
-
-"@testing-library/vue@^5.8.1":
- version "5.8.1"
- resolved "https://registry.yarnpkg.com/@testing-library/vue/-/vue-5.8.1.tgz#0292c030f99fcd40e0828f0ebed531ba35fb681f"
- integrity sha512-QX9L6dlJXi/6gfmf+yQBB9lmjGo5iI5xSwpRPJ2ma36D5aXwliHGkJg+w3aPaHDvkbEzxZSmXMt0jvP06BJZVA==
- dependencies:
- "@babel/runtime" "^7.12.5"
- "@testing-library/dom" "^7.26.6"
- "@vue/test-utils" "^1.1.0"
-
-"@types/aria-query@^4.2.0":
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
- integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==
-
-"@types/glob@^7.1.1":
- version "7.1.4"
- resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672"
- integrity sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==
- dependencies:
- "@types/minimatch" "*"
- "@types/node" "*"
-
-"@types/html-minifier-terser@^5.0.0":
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57"
- integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==
-
-"@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@^3.0.0":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff"
- integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
- dependencies:
- "@types/istanbul-lib-report" "*"
-
-"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7":
- version "7.0.8"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
- integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
-
-"@types/lodash@^4.14.171":
- version "4.14.171"
- resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
- integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
-
-"@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.5"
- resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
- integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
-
-"@types/node@*", "@types/node@>=13.7.0":
- version "16.3.2"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.2.tgz#655432817f83b51ac869c2d51dd8305fb8342e16"
- integrity sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==
-
-"@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.5"
- resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
- integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
-
-"@types/source-list-map@*":
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
- integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
-
-"@types/tapable@^1", "@types/tapable@^1.0.5":
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
- integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
-
-"@types/uglify-js@*":
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea"
- integrity sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==
- dependencies:
- source-map "^0.6.1"
-
-"@types/webpack-sources@*":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.1.1.tgz#6af17e3a3ded71eec2b98008d7c12f498a0a4506"
- integrity sha512-MjM1R6iuw8XaVbtkCBz0N349cyqBjJHCbQiOeppe3VBeFvxqs74RKHAVt9LkxTnUWc7YLZOEsUfPUnmK6SBPKQ==
- dependencies:
- "@types/node" "*"
- "@types/source-list-map" "*"
- source-map "^0.7.3"
-
-"@types/webpack@^4.41.8":
- version "4.41.30"
- resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.30.tgz#fd3db6d0d41e145a8eeeafcd3c4a7ccde9068ddc"
- integrity sha512-GUHyY+pfuQ6haAfzu4S14F+R5iGRwN6b2FRNJY7U0NilmFAqbsOfK6j1HwuLBAqwRIT+pVdNDJGJ6e8rpp0KHA==
- dependencies:
- "@types/node" "*"
- "@types/tapable" "^1"
- "@types/uglify-js" "*"
- "@types/webpack-sources" "*"
- anymatch "^3.0.0"
- source-map "^0.6.0"
-
-"@types/yargs-parser@*":
- version "20.2.1"
- resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
- integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==
-
-"@types/yargs@^15.0.0":
- version "15.0.14"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06"
- integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==
- dependencies:
- "@types/yargs-parser" "*"
-
-"@vue/component-compiler-utils@^3.1.0":
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.2.2.tgz#2f7ed5feed82ff7f0284acc11d525ee7eff22460"
- integrity sha512-rAYMLmgMuqJFWAOb3Awjqqv5X3Q3hVr4jH/kgrFJpiU0j3a90tnNBplqbj+snzrgZhC9W128z+dtgMifOiMfJg==
- dependencies:
- consolidate "^0.15.1"
- hash-sum "^1.0.2"
- lru-cache "^4.1.2"
- merge-source-map "^1.1.0"
- postcss "^7.0.36"
- 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.1.0":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.2.1.tgz#4671fc8844e09ccddb6801ceedd7b7309ae11d06"
- integrity sha512-WBRdWNJwWTodJlV9mjunTrhgfsTPI5tMuxsCxqSmQs+vyB3ccZIYixnBrkxpKRsXyah/RtEv6+kUPZhnLd9smA==
- 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"
-
-"@webpack-cli/configtest@^1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa"
- integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==
-
-"@webpack-cli/info@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b"
- integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==
- dependencies:
- envinfo "^7.7.3"
-
-"@webpack-cli/serve@^1.5.1":
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.1.tgz#b5fde2f0f79c1e120307c415a4c1d5eb15a6f278"
- integrity sha512-4vSVUiOPJLmr45S8rMGy7WDvpWxfFxfP/Qx/cxZFCfvoypTYpPPL1X8VIZMe0WTA+Jr7blUxwUSEZNkjoMTgSw==
-
-"@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.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
- integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
-
-abbrev@1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
- integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
-
-accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
- integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
- dependencies:
- mime-types "~2.1.24"
- negotiator "0.6.2"
-
-acorn-jsx@^5.2.0, acorn-jsx@^5.3.1:
- version "5.3.2"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
- integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-
-acorn@^6.4.1:
- version "6.4.2"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
- integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
-
-acorn@^7.1.1, acorn@^7.4.0:
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
- integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-
-aggregate-error@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
- integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
- dependencies:
- clean-stack "^2.0.0"
- indent-string "^4.0.0"
-
-ajv-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==
-
-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@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5:
- version "6.12.6"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
- integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
- dependencies:
- fast-deep-equal "^3.1.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
-ajv@^8.0.1:
- version "8.6.1"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.1.tgz#ae65764bf1edde8cd861281cda5057852364a295"
- integrity sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==
- dependencies:
- fast-deep-equal "^3.1.1"
- json-schema-traverse "^1.0.0"
- require-from-string "^2.0.2"
- uri-js "^4.2.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.2"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
- integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
- dependencies:
- type-fest "^0.21.3"
-
-ansi-html@0.0.7:
- version "0.0.7"
- resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
- integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
-
-ansi-regex@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
- integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
-
-ansi-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
- integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
-
-ansi-regex@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
- integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
-
-ansi-regex@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
- integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
-
-ansi-styles@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
- integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
-
-ansi-styles@^3.2.0, ansi-styles@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
- integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
- dependencies:
- color-convert "^1.9.0"
-
-ansi-styles@^4.0.0, ansi-styles@^4.1.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
- integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
- dependencies:
- color-convert "^2.0.1"
-
-anymatch@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
- integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
- dependencies:
- micromatch "^3.1.4"
- normalize-path "^2.1.1"
-
-anymatch@^3.0.0, anymatch@~3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
- integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
- 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==
-
-argparse@^1.0.7:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
- integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
- dependencies:
- sprintf-js "~1.0.2"
-
-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"
- integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
-
-arr-flatten@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
- integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
-
-arr-union@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
- integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
-
-array-flatten@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
- integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
-
-array-flatten@^2.1.0:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
- integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
-
-array-union@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
- integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
- 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"
- integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
-
-array-unique@^0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
- integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
-
-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"
- resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
- integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
- dependencies:
- object-assign "^4.1.1"
- util "0.10.3"
-
-assign-symbols@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
- integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
-
-astral-regex@^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-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.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.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==
-
-babel-loader@^8.2.2:
- version "8.2.2"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
- integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==
- dependencies:
- find-cache-dir "^3.3.1"
- loader-utils "^1.4.0"
- make-dir "^3.1.0"
- schema-utils "^2.6.5"
-
-babel-plugin-dynamic-import-node@^2.3.3:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
- integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==
- dependencies:
- object.assign "^4.1.0"
-
-babel-plugin-polyfill-corejs2@^0.2.2:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327"
- integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==
- dependencies:
- "@babel/compat-data" "^7.13.11"
- "@babel/helper-define-polyfill-provider" "^0.2.2"
- semver "^6.1.1"
-
-babel-plugin-polyfill-corejs3@^0.2.2:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b"
- integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g==
- dependencies:
- "@babel/helper-define-polyfill-provider" "^0.2.2"
- core-js-compat "^3.14.0"
-
-babel-plugin-polyfill-regenerator@^0.2.2:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077"
- integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==
- dependencies:
- "@babel/helper-define-polyfill-provider" "^0.2.2"
-
-balanced-match@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
- integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-
-base64-js@^1.0.2:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
- integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
-
-base@^0.11.1:
- version "0.11.2"
- resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
- integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
- dependencies:
- cache-base "^1.0.1"
- class-utils "^0.3.5"
- component-emitter "^1.2.1"
- define-property "^1.0.0"
- isobject "^3.0.1"
- mixin-deep "^1.2.0"
- pascalcase "^0.1.1"
-
-batch@0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
- integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
-
-big.js@^5.2.2:
- version "5.2.2"
- resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
- integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
-
-binary-extensions@^1.0.0:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
- integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
-
-binary-extensions@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
- integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
-
-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.11.9:
- version "4.12.0"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
- integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
-
-bn.js@^5.0.0, bn.js@^5.1.1:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
- integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
-
-body-parser@1.19.0:
- version "1.19.0"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
- integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
- dependencies:
- bytes "3.1.0"
- content-type "~1.0.4"
- debug "2.6.9"
- depd "~1.1.2"
- http-errors "1.7.2"
- iconv-lite "0.4.24"
- on-finished "~2.3.0"
- qs "6.7.0"
- raw-body "2.4.0"
- type-is "~1.6.17"
-
-bonjour@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5"
- integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU=
- dependencies:
- array-flatten "^2.1.0"
- deep-equal "^1.0.1"
- dns-equal "^1.0.0"
- dns-txt "^2.0.2"
- multicast-dns "^6.0.1"
- multicast-dns-service-types "^1.1.0"
-
-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=
-
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
-braces@^2.3.1, braces@^2.3.2:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
- integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
- dependencies:
- arr-flatten "^1.1.0"
- array-unique "^0.3.2"
- extend-shallow "^2.0.1"
- fill-range "^4.0.0"
- isobject "^3.0.1"
- repeat-element "^1.1.2"
- snapdragon "^0.8.1"
- snapdragon-node "^2.0.1"
- split-string "^3.0.2"
- to-regex "^3.0.1"
-
-braces@^3.0.1, braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
-brorand@^1.0.1, brorand@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
- integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
-
-browserify-aes@^1.0.0, browserify-aes@^1.0.4:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
- integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
- dependencies:
- buffer-xor "^1.0.3"
- cipher-base "^1.0.0"
- create-hash "^1.1.0"
- evp_bytestokey "^1.0.3"
- inherits "^2.0.1"
- safe-buffer "^5.0.1"
-
-browserify-cipher@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
- integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
- dependencies:
- browserify-aes "^1.0.4"
- browserify-des "^1.0.0"
- evp_bytestokey "^1.0.0"
-
-browserify-des@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
- integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
- dependencies:
- cipher-base "^1.0.1"
- des.js "^1.0.0"
- inherits "^2.0.1"
- safe-buffer "^5.1.2"
-
-browserify-rsa@^4.0.0, browserify-rsa@^4.0.1:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d"
- integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==
- dependencies:
- bn.js "^5.0.0"
- randombytes "^2.0.1"
-
-browserify-sign@^4.0.0:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3"
- integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==
- dependencies:
- bn.js "^5.1.1"
- browserify-rsa "^4.0.1"
- create-hash "^1.2.0"
- create-hmac "^1.1.7"
- elliptic "^6.5.3"
- inherits "^2.0.4"
- parse-asn1 "^5.1.5"
- readable-stream "^3.6.0"
- safe-buffer "^5.2.0"
-
-browserify-zlib@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
- integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
- dependencies:
- pako "~1.0.5"
-
-browserslist@^4.0.0, browserslist@^4.16.6:
- version "4.16.6"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
- integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
- dependencies:
- caniuse-lite "^1.0.30001219"
- colorette "^1.2.2"
- electron-to-chromium "^1.3.723"
- escalade "^3.1.1"
- node-releases "^1.1.71"
-
-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"
- resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
- integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==
-
-buffer-xor@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
- integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
-
-buffer@^4.3.0:
- 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"
- isarray "^1.0.0"
-
-builtin-status-codes@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
- integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
-
-bytes@3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
- integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
-
-bytes@3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
- integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
-
-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.2.0"
- resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389"
- integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==
- dependencies:
- "@npmcli/move-file" "^1.0.1"
- chownr "^2.0.0"
- fs-minipass "^2.0.0"
- glob "^7.1.4"
- infer-owner "^1.0.4"
- lru-cache "^6.0.0"
- minipass "^3.1.1"
- minipass-collect "^1.0.2"
- minipass-flush "^1.0.5"
- minipass-pipeline "^1.2.2"
- mkdirp "^1.0.3"
- p-map "^4.0.0"
- promise-inflight "^1.0.1"
- rimraf "^3.0.2"
- ssri "^8.0.1"
- tar "^6.0.2"
- unique-filename "^1.1.1"
-
-cache-base@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
- integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
- dependencies:
- collection-visit "^1.0.0"
- component-emitter "^1.2.1"
- get-value "^2.0.6"
- has-value "^1.0.0"
- isobject "^3.0.1"
- set-value "^2.0.0"
- to-object-path "^0.3.0"
- union-value "^1.0.0"
- unset-value "^1.0.0"
-
-call-bind@^1.0.0, call-bind@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
- integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
- dependencies:
- function-bind "^1.1.1"
- get-intrinsic "^1.0.2"
-
-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@^4.1.1:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
- integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
- dependencies:
- pascal-case "^3.1.2"
- tslib "^2.0.3"
-
-camelcase@^5.0.0:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
- integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-
-caniuse-api@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
- integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
- dependencies:
- browserslist "^4.0.0"
- caniuse-lite "^1.0.0"
- lodash.memoize "^4.1.2"
- lodash.uniq "^4.5.0"
-
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219:
- version "1.0.30001245"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz#45b941bbd833cb0fa53861ff2bae746b3c6ca5d4"
- integrity sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA==
-
-chalk@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
- integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
- dependencies:
- ansi-styles "^2.2.1"
- escape-string-regexp "^1.0.2"
- has-ansi "^2.0.0"
- strip-ansi "^3.0.0"
- supports-color "^2.0.0"
-
-chalk@^2.0.0, chalk@^2.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==
- dependencies:
- ansi-styles "^3.2.1"
- escape-string-regexp "^1.0.5"
- supports-color "^5.3.0"
-
-chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
- integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
- 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"
- braces "^2.3.2"
- glob-parent "^3.1.0"
- inherits "^2.0.3"
- is-binary-path "^1.0.0"
- is-glob "^4.0.0"
- normalize-path "^3.0.0"
- path-is-absolute "^1.0.0"
- readdirp "^2.2.1"
- upath "^1.1.1"
- optionalDependencies:
- fsevents "^1.2.7"
-
-chokidar@^3.4.1:
- version "3.5.2"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
- integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
- dependencies:
- anymatch "~3.1.2"
- braces "~3.0.2"
- glob-parent "~5.1.2"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.6.0"
- optionalDependencies:
- fsevents "~2.3.2"
-
-chownr@^1.1.1:
- 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.3"
- resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
- integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
-
-cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
- integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
- dependencies:
- inherits "^2.0.1"
- safe-buffer "^5.0.1"
-
-class-utils@^0.3.5:
- version "0.3.6"
- resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
- integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
- dependencies:
- arr-union "^3.1.0"
- define-property "^0.2.5"
- isobject "^3.0.0"
- static-extend "^0.1.1"
-
-clean-css@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
- integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
- dependencies:
- source-map "~0.6.0"
-
-clean-stack@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
- integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-
-cli-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/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
- integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
- dependencies:
- slice-ansi "^3.0.0"
- string-width "^4.2.0"
-
-cliui@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
- integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
- dependencies:
- string-width "^3.1.0"
- strip-ansi "^5.2.0"
- wrap-ansi "^5.1.0"
-
-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"
-
-collection-visit@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
- integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
- dependencies:
- map-visit "^1.0.0"
- object-visit "^1.0.0"
-
-color-convert@^1.9.0, 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.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@^1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
- integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
- dependencies:
- color-name "^1.0.0"
- simple-swizzle "^0.2.2"
-
-color@^3.0.0:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e"
- integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==
- dependencies:
- color-convert "^1.9.1"
- color-string "^1.5.4"
-
-colorette@^1.2.1, colorette@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
- integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
-
-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@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
- integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
-
-commander@^7.0.0, commander@^7.2.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
- integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
-
-commondir@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
- integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
-
-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.18"
- resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
- integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
- dependencies:
- mime-db ">= 1.43.0 < 2"
-
-compression-webpack-plugin@^6.1.1:
- version "6.1.1"
- resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-6.1.1.tgz#ae8e4b2ffdb7396bb776e66918d751a20d8ccf0e"
- integrity sha512-BEHft9M6lwOqVIQFMS/YJGmeCYXVOakC5KzQk05TFpMBlODByh1qNsZCWjUBxCQhUP9x0WfGidxTbGkjbWO/TQ==
- dependencies:
- cacache "^15.0.5"
- find-cache-dir "^3.3.1"
- schema-utils "^3.0.0"
- serialize-javascript "^5.0.1"
- 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==
- dependencies:
- accepts "~1.3.5"
- bytes "3.0.0"
- compressible "~2.0.16"
- debug "2.6.9"
- on-headers "~1.0.2"
- safe-buffer "5.1.2"
- vary "~1.1.2"
-
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
- integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-
-concat-stream@^1.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.13"
- resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
- integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
- dependencies:
- ini "^1.3.4"
- proto-list "~1.2.1"
-
-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.2.0"
- resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
- integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
-
-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"
-
-constants-browserify@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
- integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
-
-content-disposition@0.5.3:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
- integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
- dependencies:
- safe-buffer "5.1.2"
-
-content-type@~1.0.4:
- version "1.0.4"
- 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.7.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
- integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
- dependencies:
- safe-buffer "~5.1.1"
-
-cookie-signature@1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
- integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
-
-cookie@0.4.0:
- version "0.4.0"
- 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-compat@^3.14.0, core-js-compat@^3.15.0:
- version "3.15.2"
- resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb"
- integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ==
- dependencies:
- browserslist "^4.16.6"
- semver "7.0.0"
-
-core-js-pure@^3.15.0:
- version "3.15.2"
- resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.15.2.tgz#c8e0874822705f3385d3197af9348f7c9ae2e3ce"
- integrity sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA==
-
-core-js@^2.6.5:
- version "2.6.12"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
- integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
-
-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@^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.13.1"
- parse-json "^4.0.0"
-
-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.4"
- resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
- integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==
- dependencies:
- bn.js "^4.1.0"
- elliptic "^6.5.3"
-
-create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
- integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
- dependencies:
- cipher-base "^1.0.1"
- inherits "^2.0.1"
- md5.js "^1.3.4"
- ripemd160 "^2.0.1"
- sha.js "^2.4.0"
-
-create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
- version "1.1.7"
- resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
- integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
- dependencies:
- cipher-base "^1.0.3"
- create-hash "^1.1.0"
- inherits "^2.0.1"
- ripemd160 "^2.0.0"
- safe-buffer "^5.0.1"
- sha.js "^2.4.8"
-
-cross-env@^7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
- integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
- dependencies:
- cross-spawn "^7.0.1"
-
-cross-spawn@^6.0.0:
- version "6.0.5"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
- integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
- dependencies:
- nice-try "^1.0.4"
- path-key "^2.0.1"
- semver "^5.5.0"
- shebang-command "^1.2.0"
- which "^1.2.9"
-
-cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
- 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"
- integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
- dependencies:
- browserify-cipher "^1.0.0"
- browserify-sign "^4.0.0"
- create-ecdh "^4.0.0"
- create-hash "^1.1.0"
- create-hmac "^1.1.0"
- diffie-hellman "^5.0.0"
- inherits "^2.0.1"
- pbkdf2 "^3.0.3"
- public-encrypt "^4.0.0"
- randombytes "^2.0.0"
- randomfill "^1.0.3"
-
-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-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:
- postcss "^7.0.1"
- timsort "^0.3.0"
-
-css-loader@^5.2.7:
- version "5.2.7"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae"
- integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==
- dependencies:
- icss-utils "^5.1.0"
- loader-utils "^2.0.0"
- postcss "^8.2.15"
- postcss-modules-extract-imports "^3.0.0"
- postcss-modules-local-by-default "^4.0.0"
- postcss-modules-scope "^3.0.0"
- postcss-modules-values "^4.0.0"
- postcss-value-parser "^4.1.0"
- schema-utils "^3.0.0"
- semver "^7.3.5"
-
-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@^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:
- boolbase "^1.0.0"
- css-what "^3.2.1"
- domutils "^1.7.0"
- nth-check "^1.0.2"
-
-css-select@^4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
- integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==
- dependencies:
- boolbase "^1.0.0"
- css-what "^5.0.0"
- domhandler "^4.2.0"
- domutils "^2.6.0"
- nth-check "^2.0.0"
-
-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:
- mdn-data "2.0.4"
- source-map "^0.6.1"
-
-css-tree@^1.1.2:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
- integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
- dependencies:
- mdn-data "2.0.14"
- source-map "^0.6.1"
-
-css-what@^3.2.1:
- version "3.4.2"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
- integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
-
-css-what@^5.0.0:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
- integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
-
-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.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff"
- integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==
- dependencies:
- 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.3"
- postcss-unique-selectors "^4.0.1"
-
-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:
- postcss "^7.0.0"
-
-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.11"
- resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99"
- integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==
- dependencies:
- cosmiconfig "^5.0.0"
- cssnano-preset-default "^4.0.8"
- is-resolvable "^1.0.0"
- postcss "^7.0.0"
-
-csso@^4.0.2:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
- integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
- dependencies:
- css-tree "^1.1.2"
-
-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:
- 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.1, debug@^3.2.6:
- version "3.2.7"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
- integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
- dependencies:
- ms "^2.1.1"
-
-debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
- integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
- dependencies:
- ms "2.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=
-
-decode-uri-component@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
- integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
-
-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.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-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=
-
-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==
- dependencies:
- object-keys "^1.0.12"
-
-define-property@^0.2.5:
- version "0.2.5"
- resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
- integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
- dependencies:
- is-descriptor "^0.1.0"
-
-define-property@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
- integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
- dependencies:
- is-descriptor "^1.0.0"
-
-define-property@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
- integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
- dependencies:
- is-descriptor "^1.0.2"
- isobject "^3.0.1"
-
-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 "^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"
- resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
- integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
-
-des.js@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
- integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
- dependencies:
- inherits "^2.0.1"
- minimalistic-assert "^1.0.0"
-
-destroy@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
- integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
-
-detect-node@^2.0.4:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
- integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
-
-diffie-hellman@^5.0.0:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
- integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
- dependencies:
- bn.js "^4.1.0"
- miller-rabin "^4.0.0"
- randombytes "^2.0.0"
-
-dir-glob@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
- integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
- dependencies:
- path-type "^4.0.0"
-
-dns-equal@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
- integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0=
-
-dns-packet@^1.3.1:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f"
- integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==
- dependencies:
- ip "^1.1.0"
- safe-buffer "^5.0.1"
-
-dns-txt@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6"
- integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=
- 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.6:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9"
- integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==
-
-dom-converter@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
- integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
- 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.2.2"
- resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
- integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
- dependencies:
- domelementtype "^2.0.1"
- entities "^2.0.0"
-
-dom-serializer@^1.0.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
- integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
- dependencies:
- domelementtype "^2.0.1"
- domhandler "^4.2.0"
- 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:
- 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, domelementtype@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
- integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
-
-domhandler@^4.0.0, domhandler@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
- integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
- dependencies:
- domelementtype "^2.2.0"
-
-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==
- dependencies:
- dom-serializer "0"
- domelementtype "1"
-
-domutils@^2.5.2, domutils@^2.6.0:
- version "2.7.0"
- resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442"
- integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==
- dependencies:
- dom-serializer "^1.0.1"
- domelementtype "^2.2.0"
- domhandler "^4.2.0"
-
-dot-case@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
- integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
- dependencies:
- no-case "^3.0.4"
- tslib "^2.0.3"
-
-dot-prop@^5.2.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
- integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
- 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"
- integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==
- dependencies:
- commander "^2.19.0"
- lru-cache "^4.1.5"
- semver "^5.6.0"
- sigmund "^1.0.1"
-
-ee-first@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
- integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
-
-electron-to-chromium@^1.3.723:
- version "1.3.776"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.776.tgz#33f6e2423b61f1bdaa8d2a103aae78a09764a75f"
- integrity sha512-V0w7eFSBoFPpdw4xexjVPZ770UDZIevSwkkj4W97XbE3IsCsTRFpa7/yXGZ88EOQAUEA09JMMsWK0xsw0kRAYw==
-
-elliptic@^6.5.3:
- version "6.5.4"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
- integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
- dependencies:
- bn.js "^4.11.9"
- brorand "^1.1.0"
- hash.js "^1.0.0"
- hmac-drbg "^1.0.1"
- inherits "^2.0.4"
- minimalistic-assert "^1.0.1"
- minimalistic-crypto-utils "^1.0.1"
-
-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@^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=
-
-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.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
- integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==
- 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@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
- integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
-
-envinfo@^7.7.3:
- version "7.8.1"
- resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
- integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
-
-errno@^0.1.3, errno@~0.1.7:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
- integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
- dependencies:
- prr "~1.0.1"
-
-error-ex@^1.3.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
- integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
- dependencies:
- is-arrayish "^0.2.1"
-
-error-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:
- stackframe "^1.1.1"
-
-es-abstract@^1.17.2, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2:
- version "1.18.3"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0"
- integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==
- dependencies:
- call-bind "^1.0.2"
- es-to-primitive "^1.2.1"
- function-bind "^1.1.1"
- get-intrinsic "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.2"
- is-callable "^1.2.3"
- is-negative-zero "^2.0.1"
- is-regex "^1.1.3"
- is-string "^1.0.6"
- object-inspect "^1.10.3"
- object-keys "^1.1.1"
- object.assign "^4.1.2"
- string.prototype.trimend "^1.0.4"
- string.prototype.trimstart "^1.0.4"
- unbox-primitive "^1.0.1"
-
-es-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.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
- integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
-
-escape-html@~1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
- integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
-
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
- integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
-
-escape-string-regexp@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
- integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-
-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@^7.13.0:
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.13.0.tgz#6f3d232bf1fcd0428353b0d581ebaca1c5dbc17a"
- integrity sha512-u0+jL8h2MshRuMTCLslktxRsPTjlENNcNufhgHu01N982DmHVdeFniyMPoVLLRjACQOwdz3FdlsgYGBMBG+AKg==
- dependencies:
- eslint-utils "^2.1.0"
- natural-compare "^1.4.0"
- semver "^7.3.2"
- vue-eslint-parser "^7.8.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.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^4.1.1"
-
-eslint-utils@^2.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-visitor-keys@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
- integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
-
-eslint@^7.30.0:
- version "7.30.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8"
- integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==
- dependencies:
- "@babel/code-frame" "7.12.11"
- "@eslint/eslintrc" "^0.4.2"
- "@humanwhocodes/config-array" "^0.5.0"
- 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"
- escape-string-regexp "^4.0.0"
- eslint-scope "^5.1.1"
- eslint-utils "^2.1.0"
- eslint-visitor-keys "^2.0.0"
- espree "^7.3.1"
- esquery "^1.4.0"
- esutils "^2.0.2"
- fast-deep-equal "^3.1.3"
- file-entry-cache "^6.0.1"
- functional-red-black-tree "^1.0.1"
- glob-parent "^5.1.2"
- globals "^13.6.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.merge "^4.6.2"
- 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 "^6.0.9"
- 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, espree@^7.3.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6"
- integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==
- dependencies:
- acorn "^7.4.0"
- acorn-jsx "^5.3.1"
- 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.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
- integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
- dependencies:
- estraverse "^5.1.0"
-
-esrecurse@^4.1.0, esrecurse@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
- integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
- dependencies:
- estraverse "^5.2.0"
-
-estraverse@^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.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@^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.3.0"
- resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
- integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
-
-eventsource@^1.0.7:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf"
- integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==
- dependencies:
- original "^1.0.0"
-
-evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
- integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
- dependencies:
- md5.js "^1.3.4"
- safe-buffer "^5.1.1"
-
-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@^5.0.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
- integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
- dependencies:
- cross-spawn "^7.0.3"
- get-stream "^6.0.0"
- human-signals "^2.1.0"
- is-stream "^2.0.0"
- merge-stream "^2.0.0"
- npm-run-path "^4.0.1"
- onetime "^5.1.2"
- signal-exit "^3.0.3"
- strip-final-newline "^2.0.0"
-
-expand-brackets@^2.1.4:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
- integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
- dependencies:
- debug "^2.3.3"
- define-property "^0.2.5"
- extend-shallow "^2.0.1"
- posix-character-classes "^0.1.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
-express@^4.17.1:
- version "4.17.1"
- resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
- integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
- dependencies:
- accepts "~1.3.7"
- array-flatten "1.1.1"
- body-parser "1.19.0"
- content-disposition "0.5.3"
- content-type "~1.0.4"
- cookie "0.4.0"
- cookie-signature "1.0.6"
- debug "2.6.9"
- depd "~1.1.2"
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- etag "~1.8.1"
- finalhandler "~1.1.2"
- fresh "0.5.2"
- merge-descriptors "1.0.1"
- methods "~1.1.2"
- on-finished "~2.3.0"
- parseurl "~1.3.3"
- path-to-regexp "0.1.7"
- proxy-addr "~2.0.5"
- qs "6.7.0"
- range-parser "~1.2.1"
- safe-buffer "5.1.2"
- send "0.17.1"
- serve-static "1.14.1"
- setprototypeof "1.1.1"
- statuses "~1.5.0"
- type-is "~1.6.18"
- utils-merge "1.0.1"
- vary "~1.1.2"
-
-extend-shallow@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
- integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
- dependencies:
- is-extendable "^0.1.0"
-
-extend-shallow@^3.0.0, extend-shallow@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
- integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
- dependencies:
- assign-symbols "^1.0.0"
- is-extendable "^1.0.1"
-
-extglob@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
- integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
- dependencies:
- array-unique "^0.3.2"
- define-property "^1.0.0"
- expand-brackets "^2.1.4"
- extend-shallow "^2.0.1"
- fragment-cache "^0.2.1"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
-fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
- integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-
-fast-glob@^3.0.3:
- version "3.2.7"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
- integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==
- dependencies:
- "@nodelib/fs.stat" "^2.0.2"
- "@nodelib/fs.walk" "^1.2.3"
- glob-parent "^5.1.2"
- merge2 "^1.3.0"
- micromatch "^4.0.4"
-
-fast-json-stable-stringify@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
- integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-
-fast-levenshtein@^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=
-
-fastest-levenshtein@^1.0.12:
- version "1.0.12"
- resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
- integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
-
-fastq@^1.6.0:
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807"
- integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==
- dependencies:
- reusify "^1.0.4"
-
-faye-websocket@^0.11.3:
- version "0.11.4"
- resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
- integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
- dependencies:
- websocket-driver ">=0.5.1"
-
-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==
-
-file-entry-cache@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
- integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
- dependencies:
- flat-cache "^3.0.4"
-
-file-loader@^6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
- integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
- dependencies:
- loader-utils "^2.0.0"
- schema-utils "^3.0.0"
-
-file-uri-to-path@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
- integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
-
-fill-range@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
- integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
- dependencies:
- extend-shallow "^2.0.1"
- is-number "^3.0.0"
- repeat-string "^1.6.1"
- to-regex-range "^2.1.0"
-
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
-finalhandler@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
- integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
- dependencies:
- debug "2.6.9"
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- on-finished "~2.3.0"
- parseurl "~1.3.3"
- statuses "~1.5.0"
- unpipe "~1.0.0"
-
-find-cache-dir@^2.0.0, 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"
- make-dir "^2.0.0"
- pkg-dir "^3.0.0"
-
-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:
- commondir "^1.0.1"
- make-dir "^3.0.2"
- pkg-dir "^4.1.0"
-
-find-up@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
- integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
- dependencies:
- locate-path "^3.0.0"
-
-find-up@^4.0.0:
- 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"
-
-flat-cache@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
- integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
- dependencies:
- flatted "^3.1.0"
- rimraf "^3.0.2"
-
-flatted@^3.1.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05"
- integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==
-
-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.14.1"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
- integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
-
-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.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
- integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
-
-fragment-cache@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
- integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
- dependencies:
- map-cache "^0.2.2"
-
-fresh@0.5.2:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
- integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
-
-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:
- 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"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
- integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-
-fsevents@^1.2.7:
- version "1.2.13"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38"
- integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==
- dependencies:
- bindings "^1.5.0"
- nan "^2.12.1"
-
-fsevents@~2.3.2:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
- integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
-
-function-bind@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
- integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
-
-functional-red-black-tree@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
- integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
-
-gensync@^1.0.0-beta.2:
- version "1.0.0-beta.2"
- resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
- integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
-
-get-caller-file@^2.0.1:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
- integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
-
-get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
- integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
- dependencies:
- function-bind "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.1"
-
-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:
- pump "^3.0.0"
-
-get-stream@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
- integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
-
-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.18.1"
- resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.18.1.tgz#93737ca38dd6a108799ebbbce7bafc46eb80c535"
- integrity sha512-s3I3D3d0lNIflVBRFHsCoIkNfSZqBO6ivzWjNdoefBf+7TPMLgXiFoezr8M6SABiYCjZ8UXB3sIxa+ZxTKmWHw==
-
-glob-parent@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
- integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
- dependencies:
- is-glob "^3.1.0"
- path-dirname "^1.0.0"
-
-glob-parent@^5.1.2, glob-parent@~5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
- integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
- dependencies:
- is-glob "^4.0.1"
-
-glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
- version "7.1.7"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
- integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-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@^13.6.0, globals@^13.9.0:
- version "13.10.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676"
- integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==
- dependencies:
- type-fest "^0.20.2"
-
-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"
- resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
- integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
- dependencies:
- array-union "^1.0.1"
- glob "^7.0.3"
- object-assign "^4.0.1"
- pify "^2.0.0"
- pinkie-promise "^2.0.0"
-
-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.6"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
- integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
-
-handle-thing@^2.0.0:
- 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"
- resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
- integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
- dependencies:
- ansi-regex "^2.0.0"
-
-has-bigints@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
- integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
-
-has-flag@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-has-symbols@^1.0.1, has-symbols@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
- integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
-
-has-value@^0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
- integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
- dependencies:
- get-value "^2.0.3"
- has-values "^0.1.4"
- isobject "^2.0.0"
-
-has-value@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
- integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
- dependencies:
- get-value "^2.0.6"
- has-values "^1.0.0"
- isobject "^3.0.0"
-
-has-values@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
- integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
-
-has-values@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
- integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
- dependencies:
- is-number "^3.0.0"
- kind-of "^4.0.0"
-
-has@^1.0.0, has@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
- integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
- dependencies:
- function-bind "^1.1.1"
-
-hash-base@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
- integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==
- dependencies:
- inherits "^2.0.4"
- readable-stream "^3.6.0"
- safe-buffer "^5.2.0"
-
-hash-sum@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
- integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
-
-hash.js@^1.0.0, hash.js@^1.0.3:
- version "1.1.7"
- resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
- integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
- dependencies:
- inherits "^2.0.3"
- minimalistic-assert "^1.0.1"
-
-he@^1.1.0, he@^1.2.0:
- version "1.2.0"
- 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.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
- integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
- dependencies:
- hash.js "^1.0.3"
- minimalistic-assert "^1.0.0"
- minimalistic-crypto-utils "^1.0.1"
-
-hpack.js@^2.1.6:
- version "2.1.6"
- resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
- integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=
- dependencies:
- inherits "^2.0.1"
- obuf "^1.0.0"
- 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-entities@^1.3.1:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
- integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
-
-html-minifier-terser@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054"
- integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==
- dependencies:
- camel-case "^4.1.1"
- clean-css "^4.2.3"
- commander "^4.1.1"
- he "^1.2.0"
- param-case "^3.0.3"
- relateurl "^0.2.7"
- terser "^4.6.3"
-
-html-webpack-inline-source-plugin@^1.0.0-beta.2:
- version "1.0.0-beta.2"
- resolved "https://registry.yarnpkg.com/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-1.0.0-beta.2.tgz#71a9234c170ef18df6e51f4594a09b540ff03111"
- integrity sha512-ydsEKdp0tnbmnqRAH2WSSMXerCNYhjes5b79uvP2BU3p6cyk+6ucNMsw5b5xD1QxphgvBBA3QqVmdcpu8QLlRQ==
- dependencies:
- escape-string-regexp "^1.0.5"
- slash "^1.0.0"
- source-map-url "^0.4.0"
-
-html-webpack-plugin@4.5.2:
- version "4.5.2"
- resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12"
- integrity sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==
- dependencies:
- "@types/html-minifier-terser" "^5.0.0"
- "@types/tapable" "^1.0.5"
- "@types/webpack" "^4.41.8"
- html-minifier-terser "^5.0.1"
- loader-utils "^1.2.3"
- lodash "^4.17.20"
- pretty-error "^2.1.1"
- tapable "^1.1.3"
- util.promisify "1.0.0"
-
-htmlparser2@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
- integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
- dependencies:
- domelementtype "^2.0.1"
- domhandler "^4.0.0"
- domutils "^2.5.2"
- entities "^2.0.0"
-
-http-deceiver@^1.2.7:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
- integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=
-
-http-errors@1.7.2:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
- integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
- dependencies:
- depd "~1.1.2"
- inherits "2.0.3"
- setprototypeof "1.1.1"
- statuses ">= 1.5.0 < 2"
- toidentifier "1.0.0"
-
-http-errors@~1.6.2:
- version "1.6.3"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
- integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
- dependencies:
- depd "~1.1.2"
- inherits "2.0.3"
- setprototypeof "1.1.0"
- statuses ">= 1.4.0 < 2"
-
-http-errors@~1.7.2:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
- integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
- dependencies:
- depd "~1.1.2"
- inherits "2.0.4"
- setprototypeof "1.1.1"
- statuses ">= 1.5.0 < 2"
- toidentifier "1.0.0"
-
-http-parser-js@>=0.5.1:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
- integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
-
-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==
- dependencies:
- http-proxy "^1.17.0"
- is-glob "^4.0.0"
- lodash "^4.17.11"
- micromatch "^3.1.10"
-
-http-proxy@^1.17.0:
- version "1.18.1"
- resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
- integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
- dependencies:
- eventemitter3 "^4.0.0"
- follow-redirects "^1.0.0"
- requires-port "^1.0.0"
-
-https-browserify@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
- integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
-
-human-signals@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
- integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
-
-husky@^7.0.0:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.1.tgz#579f4180b5da4520263e8713cc832942b48e1f1c"
- integrity sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA==
-
-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"
-
-iconv-lite@^0.6.2:
- version "0.6.3"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
- integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
- dependencies:
- safer-buffer ">= 2.1.2 < 3.0.0"
-
-icss-utils@^5.0.0, icss-utils@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
- integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
-
-ieee754@^1.1.4:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
- integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
-
-iferr@^0.1.5:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
- integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
-
-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:
- caller-path "^2.0.0"
- resolve-from "^3.0.0"
-
-import-fresh@^3.0.0, import-fresh@^3.2.1:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
- integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
- 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"
-
-import-local@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6"
- integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==
- dependencies:
- pkg-dir "^4.2.0"
- resolve-cwd "^3.0.0"
-
-imurmurhash@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
- integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
-
-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"
- integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.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==
-
-inherits@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
- integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
-
-inherits@2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
- integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
-
-ini@^1.3.4:
- version "1.3.8"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
- integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
-
-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:
- default-gateway "^4.2.0"
- ipaddr.js "^1.9.0"
-
-interpret@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
- integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
-
-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.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"
- integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
- dependencies:
- kind-of "^3.0.2"
-
-is-accessor-descriptor@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
- integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
- dependencies:
- kind-of "^6.0.0"
-
-is-arguments@^1.0.4:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"
- integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==
- dependencies:
- call-bind "^1.0.0"
-
-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-bigint@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a"
- integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==
-
-is-binary-path@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
- integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
- dependencies:
- binary-extensions "^1.0.0"
-
-is-binary-path@~2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
- integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
- dependencies:
- binary-extensions "^2.0.0"
-
-is-boolean-object@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8"
- integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==
- dependencies:
- call-bind "^1.0.2"
-
-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, is-callable@^1.2.3:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
- integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
-
-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-core-module@^2.2.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491"
- integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==
- dependencies:
- has "^1.0.3"
-
-is-data-descriptor@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
- integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
- dependencies:
- kind-of "^3.0.2"
-
-is-data-descriptor@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
- integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
- dependencies:
- kind-of "^6.0.0"
-
-is-date-object@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5"
- integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==
-
-is-descriptor@^0.1.0:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
- integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
- dependencies:
- is-accessor-descriptor "^0.1.6"
- is-data-descriptor "^0.1.4"
- kind-of "^5.0.0"
-
-is-descriptor@^1.0.0, is-descriptor@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
- integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
- dependencies:
- is-accessor-descriptor "^1.0.0"
- is-data-descriptor "^1.0.0"
- kind-of "^6.0.2"
-
-is-directory@^0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
- integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
-
-is-extendable@^0.1.0, is-extendable@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
- integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
-
-is-extendable@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
- integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
- dependencies:
- is-plain-object "^2.0.4"
-
-is-extglob@^2.1.0, is-extglob@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
- integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
-
-is-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"
- integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
- dependencies:
- is-extglob "^2.1.0"
-
-is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
- integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
- dependencies:
- is-extglob "^2.1.1"
-
-is-negative-zero@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
- integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
-
-is-number-object@^1.0.4:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb"
- integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==
-
-is-number@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
- integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
- dependencies:
- kind-of "^3.0.2"
-
-is-number@^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-obj@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
- integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
-
-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:
- 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-object@^2.0.3, is-plain-object@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
- integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
- dependencies:
- isobject "^3.0.1"
-
-is-regex@^1.0.4, is-regex@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
- integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==
- dependencies:
- call-bind "^1.0.2"
- has-symbols "^1.0.2"
-
-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-string@^1.0.5, is-string@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f"
- integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==
-
-is-symbol@^1.0.2, is-symbol@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
- integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
- dependencies:
- has-symbols "^1.0.2"
-
-is-unicode-supported@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
- integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
-
-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.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
- integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
-
-is-wsl@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
- integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
-
-isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
- integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
-
-isexe@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
- integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-
-isobject@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
- integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
- dependencies:
- isarray "1.0.0"
-
-isobject@^3.0.0, isobject@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
- integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
-
-jasmine-core@~3.8.0:
- version "3.8.0"
- resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.8.0.tgz#815399aae5aa5d9beeb1262805f981b99ffc9bf0"
- integrity sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg==
-
-jasmine@^3.8.0:
- version "3.8.0"
- resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.8.0.tgz#4497bc797eede7ca9de18179aedd4cf50245d8dc"
- integrity sha512-kdQ3SfcNpMbbMdgJPLyFe9IksixdnrgYaCJapP9sS0aLgdWdIZADNXEr+11Zafxm1VDfRSC5ZL4fzXT0bexzXw==
- dependencies:
- glob "^7.1.6"
- jasmine-core "~3.8.0"
-
-js-beautify@^1.6.12:
- version "1.14.0"
- resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.0.tgz#2ce790c555d53ce1e3d7363227acf5dc69024c2d"
- integrity sha512-yuck9KirNSCAwyNJbqW+BxJqJ0NLJ4PwBUzQQACl5O3qHMBXVkXb/rD0ilh/Lat/tn88zSZ+CAHOlk0DsY7GuQ==
- dependencies:
- config-chain "^1.1.12"
- editorconfig "^0.15.3"
- glob "^7.1.3"
- nopt "^5.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-yaml@^3.13.1:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
- integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-jsesc@^2.5.1:
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
- integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
-
-jsesc@~0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
- integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
-
-json-parse-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-parse-even-better-errors@^2.3.0:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
- integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
-
-json-schema-traverse@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
- integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json-schema-traverse@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
- integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
-
-json-stable-stringify-without-jsonify@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
- integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
-
-json3@^3.3.3:
- version "3.3.3"
- resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
- integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
-
-json5@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
- integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
- dependencies:
- minimist "^1.2.0"
-
-json5@^2.1.2:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
- integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
- dependencies:
- minimist "^1.2.5"
-
-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.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9"
- integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==
- 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==
-
-kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
- integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
- dependencies:
- is-buffer "^1.1.5"
-
-kind-of@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
- integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
- dependencies:
- is-buffer "^1.1.5"
-
-kind-of@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
- integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
-
-kind-of@^6.0.0, kind-of@^6.0.2:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
- integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
-
-kotlin-compiler@^1.3.61:
- version "1.5.21"
- resolved "https://registry.yarnpkg.com/kotlin-compiler/-/kotlin-compiler-1.5.21.tgz#6bbba315c1cc31b0f00e13c52c9cd5ec4e8defb7"
- integrity sha512-bmO0HI1ojCpbzWsPvhniDUWBDK6OZcSKG2C2uYrDsw9/91gL4W857ckRvHHFZfUEhPdr63Fgrc+8teVfEnWIzQ==
-
-kotlin@^1.5.21:
- version "1.5.21"
- resolved "https://registry.yarnpkg.com/kotlin/-/kotlin-1.5.21.tgz#07f11f4bae47815e9dce55880895f1452293d95f"
- integrity sha512-U6PXruPv/YP7Ar+8ScfurSKftcv5QoB2o517puswGotrRvxrT9WnrtNiRdd5pmG9GasBJau9/PemGx83FpTJ0g==
-
-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:
- lodash "^4.17.5"
- webpack-sources "^1.1.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@^11.0.1:
- version "11.0.1"
- resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.0.1.tgz#1b8ae8ed5a52ed87252db95fe008c2618c85f55a"
- integrity sha512-RkTA1ulE6jAGFskxpGAwxfVRXjHp7D9gFg/+KMARUWMPiVFP0t28Em2u0gL8sA0w3/ck3TC57F2v2RNeQ5XPnw==
- dependencies:
- chalk "^4.1.1"
- cli-truncate "^2.1.0"
- commander "^7.2.0"
- cosmiconfig "^7.0.0"
- debug "^4.3.1"
- dedent "^0.7.0"
- enquirer "^2.3.6"
- execa "^5.0.0"
- listr2 "^3.8.2"
- log-symbols "^4.1.0"
- micromatch "^4.0.4"
- normalize-path "^3.0.0"
- please-upgrade-node "^3.2.0"
- string-argv "0.3.1"
- stringify-object "^3.3.0"
-
-listr2@^3.8.2:
- version "3.10.0"
- resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.10.0.tgz#58105a53ed7fa1430d1b738c6055ef7bb006160f"
- integrity sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw==
- dependencies:
- cli-truncate "^2.1.0"
- colorette "^1.2.2"
- log-update "^4.0.0"
- p-map "^4.0.0"
- rxjs "^6.6.7"
- through "^2.3.8"
- wrap-ansi "^7.0.0"
-
-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@^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 "^3.0.0"
- json5 "^1.0.1"
-
-loader-utils@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
- integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
- dependencies:
- 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"
-
-locate-path@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
- integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
- dependencies:
- p-locate "^4.1.0"
-
-lodash.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.debounce@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
- integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
-
-lodash.memoize@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
- integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
-
-lodash.merge@^4.6.2:
- version "4.6.2"
- resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
- integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-
-lodash.truncate@^4.4.2:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
- integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
-
-lodash.uniq@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
- integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-
-lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5:
- version "4.17.21"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
- integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-
-log-symbols@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
- integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
- dependencies:
- chalk "^4.1.0"
- is-unicode-supported "^0.1.0"
-
-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.6.8:
- version "1.7.1"
- resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
- integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
-
-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==
-
-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"
-
-lower-case@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
- integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
- dependencies:
- tslib "^2.0.3"
-
-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==
- dependencies:
- 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"
-
-lz-string@^1.4.4:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
- integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
-
-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, make-dir@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
- integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
- dependencies:
- semver "^6.0.0"
-
-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-visit@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
- integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
- dependencies:
- object-visit "^1.0.0"
-
-md5.js@^1.3.4:
- version "1.3.5"
- resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
- integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
- dependencies:
- hash-base "^3.0.0"
- inherits "^2.0.1"
- safe-buffer "^5.1.2"
-
-mdn-data@2.0.14:
- version "2.0.14"
- resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
- integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
-
-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==
-
-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.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=
- dependencies:
- errno "^0.1.3"
- readable-stream "^2.0.1"
-
-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:
- 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:
- version "3.1.10"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
- integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
- dependencies:
- arr-diff "^4.0.0"
- array-unique "^0.3.2"
- braces "^2.3.1"
- define-property "^2.0.2"
- extend-shallow "^3.0.2"
- extglob "^2.0.4"
- fragment-cache "^0.2.1"
- kind-of "^6.0.2"
- nanomatch "^1.2.9"
- object.pick "^1.3.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.2"
-
-micromatch@^4.0.0, micromatch@^4.0.4:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
- integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
- dependencies:
- braces "^3.0.1"
- picomatch "^2.2.3"
-
-miller-rabin@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
- integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
- dependencies:
- bn.js "^4.0.0"
- brorand "^1.0.1"
-
-mime-db@1.48.0, "mime-db@>= 1.43.0 < 2":
- version "1.48.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d"
- integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==
-
-mime-types@~2.1.17, mime-types@~2.1.24:
- version "2.1.31"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b"
- integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==
- dependencies:
- mime-db "1.48.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.5.2"
- resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
- integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
-
-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@^1.6.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8"
- integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==
- dependencies:
- loader-utils "^2.0.0"
- schema-utils "^3.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"
- integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-
-minimalistic-crypto-utils@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
- integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
-
-minimatch@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimist@^1.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==
-
-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:
- minipass "^3.0.0"
-
-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 "^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"
- resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
- integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
- dependencies:
- for-in "^1.0.2"
- is-extendable "^1.0.1"
-
-mkdirp@^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 "^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"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
- integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
-
-ms@2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
- integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-ms@^2.1.1:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
- integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-
-multicast-dns-service-types@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
- integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=
-
-multicast-dns@^6.0.1:
- version "6.2.3"
- resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229"
- integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==
- dependencies:
- dns-packet "^1.3.1"
- thunky "^1.0.2"
-
-nan@^2.12.1:
- version "2.14.2"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
- integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
-
-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==
-
-nanoid@^3.1.23:
- version "3.1.23"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
- integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
-
-nanomatch@^1.2.9:
- version "1.2.13"
- resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
- integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
- dependencies:
- arr-diff "^4.0.0"
- array-unique "^0.3.2"
- define-property "^2.0.2"
- extend-shallow "^3.0.2"
- fragment-cache "^0.2.1"
- is-windows "^1.0.2"
- kind-of "^6.0.2"
- object.pick "^1.3.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
-natural-compare@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
- integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
-
-negotiator@0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
- integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
-
-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@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
- integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
- dependencies:
- lower-case "^2.0.2"
- tslib "^2.0.3"
-
-node-fetch@^2.3.0:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
- integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
-
-node-forge@^0.10.0:
- version "0.10.0"
- resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
- integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
-
-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==
- dependencies:
- assert "^1.1.1"
- browserify-zlib "^0.2.0"
- buffer "^4.3.0"
- console-browserify "^1.1.0"
- constants-browserify "^1.0.0"
- crypto-browserify "^3.11.0"
- domain-browser "^1.1.1"
- events "^3.0.0"
- https-browserify "^1.0.0"
- os-browserify "^0.3.0"
- path-browserify "0.0.1"
- process "^0.11.10"
- punycode "^1.2.4"
- querystring-es3 "^0.2.0"
- readable-stream "^2.3.3"
- stream-browserify "^2.0.1"
- stream-http "^2.7.2"
- string_decoder "^1.0.0"
- timers-browserify "^2.0.4"
- tty-browserify "0.0.0"
- url "^0.11.0"
- util "^0.11.0"
- vm-browserify "^1.0.1"
-
-node-modules-regexp@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
- integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
-
-node-releases@^1.1.71:
- version "1.1.73"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20"
- integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==
-
-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"
-
-normalize-path@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
- integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
- dependencies:
- remove-trailing-separator "^1.0.1"
-
-normalize-path@^3.0.0, normalize-path@~3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
- integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-
-normalize-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-run-path@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
- integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
- dependencies:
- path-key "^2.0.0"
-
-npm-run-path@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
- integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
- dependencies:
- path-key "^3.0.0"
-
-nth-check@^1.0.2:
- 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"
-
-nth-check@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
- integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
- dependencies:
- boolbase "^1.0.0"
-
-object-assign@^4.0.1, object-assign@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
- integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
-
-object-copy@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
- integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
- dependencies:
- copy-descriptor "^0.1.0"
- define-property "^0.2.5"
- kind-of "^3.0.3"
-
-object-inspect@^1.10.3:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
- integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
-
-object-is@^1.0.1:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
- integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
-
-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==
-
-object-visit@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
- integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
- dependencies:
- isobject "^3.0.0"
-
-object.assign@^4.1.0, object.assign@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
- integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
- dependencies:
- call-bind "^1.0.0"
- define-properties "^1.1.3"
- has-symbols "^1.0.1"
- object-keys "^1.1.1"
-
-object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7"
- integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.18.0-next.2"
-
-object.pick@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
- integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
- dependencies:
- isobject "^3.0.1"
-
-object.values@^1.1.0:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30"
- integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.18.2"
-
-obuf@^1.0.0, obuf@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
- integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
-
-on-finished@~2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
- integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
- dependencies:
- ee-first "1.1.1"
-
-on-headers@~1.0.2:
- version "1.0.2"
- 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.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"
-
-onetime@^5.1.0, onetime@^5.1.2:
- 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"
-
-optimize-css-assets-webpack-plugin@^5.0.3:
- version "5.0.8"
- resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.8.tgz#cbccdcf5a6ef61d4f8cc78cf083a67446e5f402a"
- integrity sha512-mgFS1JdOtEGzD8l+EuISqL57cKO+We9GcoiQEmdCWRqqck+FGNmYJtx9qfAPzEz+lRrlThWMuGDaRkI/yWNx/Q==
- 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==
- dependencies:
- url-parse "^1.4.3"
-
-os-browserify@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
- integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
-
-p-finally@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
- integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
-
-p-limit@^2.0.0, p-limit@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
- integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
- dependencies:
- p-try "^2.0.0"
-
-p-locate@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
- integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
- dependencies:
- p-limit "^2.0.0"
-
-p-locate@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
- integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
- dependencies:
- p-limit "^2.2.0"
-
-p-map@^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@^3.0.3:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
- integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
- dependencies:
- dot-case "^3.0.4"
- tslib "^2.0.3"
-
-parent-module@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
- integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
- dependencies:
- callsites "^3.0.0"
-
-parse-asn1@^5.0.0, parse-asn1@^5.1.5:
- version "5.1.6"
- resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
- integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==
- dependencies:
- asn1.js "^5.2.0"
- browserify-aes "^1.0.0"
- evp_bytestokey "^1.0.0"
- pbkdf2 "^3.0.3"
- safe-buffer "^5.1.1"
-
-parse-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.3.1"
- json-parse-better-errors "^1.0.1"
-
-parse-json@^5.0.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
- integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
- 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"
-
-parseurl@~1.3.2, parseurl@~1.3.3:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
- integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
-
-pascal-case@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
- integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
- dependencies:
- no-case "^3.0.4"
- tslib "^2.0.3"
-
-pascalcase@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
- integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
-
-path-browserify@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
- integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
-
-path-dirname@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
- integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
-
-path-exists@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
- integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
-
-path-exists@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
- integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-
-path-is-absolute@^1.0.0:
- 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.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.7"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
- integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-
-path-to-regexp@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
- integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
-
-path-type@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
- integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-
-pbkdf2@^3.0.3:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
- integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
- dependencies:
- create-hash "^1.1.2"
- create-hmac "^1.1.4"
- ripemd160 "^2.0.1"
- safe-buffer "^5.0.1"
- sha.js "^2.4.8"
-
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
- integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
-
-pify@^2.0.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
- integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
-
-pify@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
- integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
-
-pinkie-promise@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
- integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
- dependencies:
- pinkie "^2.0.0"
-
-pinkie@^2.0.0:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
- integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
-
-pirates@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
- integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
- dependencies:
- node-modules-regexp "^1.0.0"
-
-pkg-dir@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
- integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
- dependencies:
- find-up "^3.0.0"
-
-pkg-dir@^4.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:
- 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@^7.0.1:
- version "7.0.5"
- resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e"
- integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==
- dependencies:
- postcss "^7.0.27"
- postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.0.2"
-
-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:
- 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@^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 "^7.0.0"
- postcss-value-parser "^3.0.0"
-
-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 "^7.0.0"
-
-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 "^7.0.0"
-
-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 "^7.0.0"
-
-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 "^7.0.0"
-
-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:
- css-color-names "0.0.4"
- postcss "^7.0.0"
- postcss-value-parser "^3.0.0"
- stylehacks "^4.0.0"
-
-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:
- 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-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:
- postcss "^7.0.0"
- postcss-value-parser "^3.0.0"
-
-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:
- 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@^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.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@^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.0"
- has "^1.0.0"
- postcss "^7.0.0"
- postcss-selector-parser "^3.0.0"
-
-postcss-modules-extract-imports@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
- integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
-
-postcss-modules-local-by-default@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
- integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
- dependencies:
- icss-utils "^5.0.0"
- postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.1.0"
-
-postcss-modules-scope@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
- integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
- dependencies:
- postcss-selector-parser "^6.0.4"
-
-postcss-modules-values@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
- integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
- dependencies:
- icss-utils "^5.0.0"
-
-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 "^7.0.0"
-
-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 "^3.0.0"
- postcss "^7.0.0"
- postcss-value-parser "^3.0.0"
-
-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 "^7.0.0"
- postcss-value-parser "^3.0.0"
-
-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:
- cssnano-util-get-arguments "^4.0.0"
- postcss "^7.0.0"
- postcss-value-parser "^3.0.0"
-
-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:
- browserslist "^4.0.0"
- caniuse-api "^3.0.0"
- has "^1.0.0"
- postcss "^7.0.0"
-
-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:
- cssnano-util-get-match "^4.0.0"
- has "^1.0.0"
- postcss "^7.0.0"
- postcss-value-parser "^3.0.0"
-
-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:
- dot-prop "^5.2.0"
- indexes-of "^1.0.1"
- uniq "^1.0.1"
-
-postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
- version "6.0.6"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea"
- integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==
- dependencies:
- cssesc "^3.0.0"
- util-deprecate "^1.0.2"
-
-postcss-svgo@^4.0.3:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e"
- integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==
- dependencies:
- 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.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-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@^7.0.0, postcss@^7.0.1, postcss@^7.0.27, postcss@^7.0.36:
- version "7.0.36"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb"
- integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==
- dependencies:
- chalk "^2.4.2"
- source-map "^0.6.1"
- supports-color "^6.1.0"
-
-postcss@^8.2.15:
- version "8.3.5"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
- integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==
- dependencies:
- colorette "^1.2.2"
- nanoid "^3.1.23"
- source-map-js "^0.6.2"
-
-prelude-ls@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
- integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-
-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.1.1:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"
- integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==
- dependencies:
- lodash "^4.17.20"
- renderkid "^2.0.4"
-
-pretty-format@^26.6.2:
- version "26.6.2"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
- integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
- dependencies:
- "@jest/types" "^26.6.2"
- ansi-regex "^5.0.0"
- ansi-styles "^4.0.0"
- react-is "^17.0.1"
-
-pretty@^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"
- resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
- integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
-
-process@^0.11.10:
- version "0.11.10"
- resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
- integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
-
-progress@^2.0.0:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
- integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-
-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.11.2:
- version "6.11.2"
- resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b"
- integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==
- dependencies:
- "@protobufjs/aspromise" "^1.1.2"
- "@protobufjs/base64" "^1.1.2"
- "@protobufjs/codegen" "^2.0.4"
- "@protobufjs/eventemitter" "^1.1.0"
- "@protobufjs/fetch" "^1.1.0"
- "@protobufjs/float" "^1.0.2"
- "@protobufjs/inquire" "^1.1.0"
- "@protobufjs/path" "^1.1.2"
- "@protobufjs/pool" "^1.1.0"
- "@protobufjs/utf8" "^1.1.0"
- "@types/long" "^4.0.1"
- "@types/node" ">=13.7.0"
- long "^4.0.0"
-
-proxy-addr@~2.0.5:
- version "2.0.7"
- resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
- integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
- dependencies:
- forwarded "0.2.0"
- ipaddr.js "1.9.1"
-
-prr@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
- integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
-
-pseudomap@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
- integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
-
-public-encrypt@^4.0.0:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
- integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
- dependencies:
- bn.js "^4.1.0"
- browserify-rsa "^4.0.0"
- create-hash "^1.1.0"
- parse-asn1 "^5.0.0"
- randombytes "^2.0.1"
- safe-buffer "^5.1.2"
-
-pump@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
- integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
- dependencies:
- end-of-stream "^1.1.0"
- once "^1.3.1"
-
-pump@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
- integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
- dependencies:
- end-of-stream "^1.1.0"
- once "^1.3.1"
-
-pumpify@^1.3.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"
- integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
-
-punycode@^1.2.4:
- version "1.4.1"
- 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"
- integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
-
-qs@6.7.0:
- version "6.7.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
- integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
-
-querystring-es3@^0.2.0:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
- integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
-
-querystring@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
- integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
-
-querystringify@^2.1.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
- integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
-
-queue-microtask@^1.2.2:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
- integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-
-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==
- dependencies:
- safe-buffer "^5.1.0"
-
-randomfill@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
- integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
- dependencies:
- randombytes "^2.0.5"
- safe-buffer "^5.1.0"
-
-range-parser@^1.2.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==
-
-raw-body@2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
- integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
- dependencies:
- bytes "3.1.0"
- http-errors "1.7.2"
- iconv-lite "0.4.24"
- unpipe "1.0.0"
-
-react-is@^17.0.1:
- version "17.0.2"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
- integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-
-"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"
- isarray "~1.0.0"
- process-nextick-args "~2.0.0"
- safe-buffer "~5.1.1"
- string_decoder "~1.1.1"
- util-deprecate "~1.0.1"
-
-readable-stream@^3.0.6, readable-stream@^3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
- integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
- dependencies:
- inherits "^2.0.3"
- string_decoder "^1.1.1"
- util-deprecate "^1.0.1"
-
-readdirp@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
- integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
- dependencies:
- graceful-fs "^4.1.11"
- micromatch "^3.1.10"
- readable-stream "^2.0.2"
-
-readdirp@~3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
- integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
- dependencies:
- picomatch "^2.2.1"
-
-rechoir@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
- integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==
- dependencies:
- resolve "^1.9.0"
-
-regenerate-unicode-properties@^8.2.0:
- version "8.2.0"
- resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
- integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==
- dependencies:
- regenerate "^1.4.0"
-
-regenerate@^1.4.0:
- version "1.4.2"
- resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
- integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
-
-regenerator-runtime@^0.13.4:
- version "0.13.7"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
- integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
-
-regenerator-transform@^0.14.2:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4"
- integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==
- dependencies:
- "@babel/runtime" "^7.8.4"
-
-regex-not@^1.0.0, regex-not@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
- integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
- dependencies:
- extend-shallow "^3.0.2"
- safe-regex "^1.1.0"
-
-regexp.prototype.flags@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"
- integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
-
-regexpp@^3.1.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
- integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
-
-regexpu-core@^4.7.1:
- version "4.7.1"
- resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
- integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==
- dependencies:
- regenerate "^1.4.0"
- regenerate-unicode-properties "^8.2.0"
- regjsgen "^0.5.1"
- regjsparser "^0.6.4"
- unicode-match-property-ecmascript "^1.0.4"
- unicode-match-property-value-ecmascript "^1.2.0"
-
-regjsgen@^0.5.1:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733"
- integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==
-
-regjsparser@^0.6.4:
- version "0.6.9"
- resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6"
- integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==
- dependencies:
- jsesc "~0.5.0"
-
-relateurl@^0.2.7:
- version "0.2.7"
- resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
- integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
-
-remove-trailing-separator@^1.0.1:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
- integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
-
-renderkid@^2.0.4:
- version "2.0.7"
- resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609"
- integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==
- dependencies:
- css-select "^4.1.3"
- dom-converter "^0.2.0"
- htmlparser2 "^6.1.0"
- lodash "^4.17.21"
- strip-ansi "^3.0.1"
-
-repeat-element@^1.1.2:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9"
- integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==
-
-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=
-
-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@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
- integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
-
-require-main-filename@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
- integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
-
-requires-port@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
- integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
-
-resolve-cwd@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
- integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=
- dependencies:
- resolve-from "^3.0.0"
-
-resolve-cwd@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
- integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
- dependencies:
- resolve-from "^5.0.0"
-
-resolve-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-from@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
- integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-
-resolve-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.14.2, resolve@^1.9.0:
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
- integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
- dependencies:
- is-core-module "^2.2.0"
- 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==
-
-retry@^0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
- integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
-
-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.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"
- integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
- dependencies:
- hash-base "^3.0.0"
- inherits "^2.0.1"
-
-run-parallel@^1.1.9:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
- integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
- dependencies:
- queue-microtask "^1.2.2"
-
-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.7:
- version "6.6.7"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
- integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
- 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, 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"
- resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
- integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
- dependencies:
- ret "~0.1.10"
-
-"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.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:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
- integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-
-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 "^6.1.0"
- ajv-errors "^1.0.0"
- ajv-keywords "^3.1.0"
-
-schema-utils@^2.6.5:
- 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"
-
-schema-utils@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.0.tgz#95986eb604f66daadeed56e379bfe7a7f963cdb9"
- integrity sha512-tTEaeYkyIhEZ9uWgAjDerWov3T9MgX8dhhy2r0IGeeX4W8ngtGl1++dUve/RUqzuaASSh7shwCDJjEzthxki8w==
- dependencies:
- "@types/json-schema" "^7.0.7"
- ajv "^6.12.5"
- 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.10.8:
- version "1.10.11"
- resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9"
- integrity sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==
- dependencies:
- node-forge "^0.10.0"
-
-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@7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
- integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
-
-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.1.1, semver@^6.1.2, semver@^6.3.0:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
- integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-
-semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
- version "7.3.5"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
- integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
- dependencies:
- lru-cache "^6.0.0"
-
-send@0.17.1:
- version "0.17.1"
- resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
- integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
- dependencies:
- debug "2.6.9"
- depd "~1.1.2"
- destroy "~1.0.4"
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- etag "~1.8.1"
- fresh "0.5.2"
- http-errors "~1.7.2"
- mime "1.6.0"
- ms "2.1.1"
- on-finished "~2.3.0"
- range-parser "~1.2.1"
- statuses "~1.5.0"
-
-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"
-
-serialize-javascript@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
- integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
- 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"
- integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=
- dependencies:
- accepts "~1.3.4"
- batch "0.6.1"
- debug "2.6.9"
- escape-html "~1.0.3"
- http-errors "~1.6.2"
- mime-types "~2.1.17"
- parseurl "~1.3.2"
-
-serve-static@1.14.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
- integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
- dependencies:
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- parseurl "~1.3.3"
- send "0.17.1"
-
-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"
- integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
- dependencies:
- extend-shallow "^2.0.1"
- is-extendable "^0.1.1"
- is-plain-object "^2.0.3"
- split-string "^3.0.1"
-
-setimmediate@^1.0.4:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
- integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
-
-setprototypeof@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
- integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
-
-setprototypeof@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
- integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
-
-sha.js@^2.4.0, sha.js@^2.4.8:
- version "2.4.11"
- resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
- integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
- dependencies:
- inherits "^2.0.1"
- safe-buffer "^5.0.1"
-
-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"
- integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
- dependencies:
- shebang-regex "^1.0.0"
-
-shebang-command@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
- integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
- dependencies:
- shebang-regex "^3.0.0"
-
-shebang-regex@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
- integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
-
-shebang-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
- integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
-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, signal-exit@^3.0.2, signal-exit@^3.0.3:
- 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@^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"
- integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
- dependencies:
- define-property "^1.0.0"
- isobject "^3.0.0"
- snapdragon-util "^3.0.1"
-
-snapdragon-util@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
- integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
- dependencies:
- kind-of "^3.2.0"
-
-snapdragon@^0.8.1:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
- integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
- dependencies:
- base "^0.11.1"
- debug "^2.2.0"
- define-property "^0.2.5"
- extend-shallow "^2.0.1"
- map-cache "^0.2.2"
- source-map "^0.5.6"
- source-map-resolve "^0.5.0"
- use "^3.1.0"
-
-sockjs-client@^1.5.0:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.1.tgz#256908f6d5adfb94dabbdbd02c66362cca0f9ea6"
- integrity sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==
- dependencies:
- debug "^3.2.6"
- eventsource "^1.0.7"
- faye-websocket "^0.11.3"
- inherits "^2.0.4"
- json3 "^3.3.3"
- url-parse "^1.5.1"
-
-sockjs@^0.3.21:
- version "0.3.21"
- resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417"
- integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==
- dependencies:
- faye-websocket "^0.11.3"
- uuid "^3.4.0"
- websocket-driver "^0.7.4"
-
-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-js@^0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
- integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
-
-source-map-loader@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.3.tgz#7dbc2fe7ea09d3e43c51fd9fc478b7f016c1f820"
- integrity sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA==
- dependencies:
- abab "^2.0.5"
- iconv-lite "^0.6.2"
- loader-utils "^2.0.0"
- schema-utils "^3.0.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.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:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
-source-map-url@^0.4.0:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
- integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
-
-source-map@^0.5.0, 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.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
- integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
-source-map@^0.7.3:
- version "0.7.3"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
- integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
-spdy-transport@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
- integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
- dependencies:
- debug "^4.1.0"
- detect-node "^2.0.4"
- hpack.js "^2.1.6"
- obuf "^1.1.2"
- readable-stream "^3.0.6"
- wbuf "^1.7.3"
-
-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"
- http-deceiver "^1.2.7"
- select-hose "^2.0.0"
- spdy-transport "^3.0.0"
-
-split-string@^3.0.1, split-string@^3.0.2:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
- integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
- dependencies:
- extend-shallow "^3.0.0"
-
-sprintf-js@~1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
- integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-
-ssri@^6.0.1:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5"
- integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==
- dependencies:
- figgy-pudding "^3.5.1"
-
-ssri@^8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
- integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
- 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"
- integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
- dependencies:
- define-property "^0.2.5"
- object-copy "^0.1.0"
-
-"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
- integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
-
-stream-browserify@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
- integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
- dependencies:
- 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"
- integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
- dependencies:
- builtin-status-codes "^3.0.0"
- inherits "^2.0.1"
- readable-stream "^2.3.6"
- 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==
-
-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@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
- integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
- dependencies:
- is-fullwidth-code-point "^2.0.0"
- strip-ansi "^4.0.0"
-
-string-width@^3.0.0, string-width@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
- integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
- dependencies:
- emoji-regex "^7.0.1"
- is-fullwidth-code-point "^2.0.0"
- strip-ansi "^5.1.0"
-
-string-width@^4.1.0, string-width@^4.2.0:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
- integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.0"
-
-string.prototype.trimend@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
- integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
-
-string.prototype.trimstart@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
- integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
-
-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"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
- integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
- dependencies:
- safe-buffer "~5.1.0"
-
-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"
- integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
- dependencies:
- ansi-regex "^2.0.0"
-
-strip-ansi@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
- integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
- dependencies:
- ansi-regex "^3.0.0"
-
-strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
- integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
- dependencies:
- ansi-regex "^4.1.0"
-
-strip-ansi@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
- integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
- dependencies:
- ansi-regex "^5.0.0"
-
-strip-eof@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
- integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
-
-strip-final-newline@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
- integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-
-strip-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==
-
-style-loader@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
- integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==
- dependencies:
- loader-utils "^2.0.0"
- schema-utils "^3.0.0"
-
-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@^5.3.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
- integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
- dependencies:
- has-flag "^3.0.0"
-
-supports-color@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
- integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
- dependencies:
- has-flag "^3.0.0"
-
-supports-color@^7.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"
- object.values "^1.1.0"
- sax "~1.2.4"
- stable "^0.1.8"
- unquote "~1.1.1"
- util.promisify "~1.0.0"
-
-table@^6.0.9:
- version "6.7.1"
- resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
- integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==
- dependencies:
- ajv "^8.0.1"
- lodash.clonedeep "^4.5.0"
- lodash.truncate "^4.4.2"
- slice-ansi "^4.0.0"
- string-width "^4.2.0"
- strip-ansi "^6.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.1.0"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83"
- integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==
- 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, terser@^4.6.3:
- 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.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.12"
- resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
- integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==
- 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@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
- integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
-
-to-object-path@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
- integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
- dependencies:
- kind-of "^3.0.2"
-
-to-regex-range@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
- integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
- dependencies:
- is-number "^3.0.0"
- repeat-string "^1.6.1"
-
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
-to-regex@^3.0.1, to-regex@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
- integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
- dependencies:
- define-property "^2.0.2"
- extend-shallow "^3.0.2"
- regex-not "^1.0.2"
- safe-regex "^1.1.0"
-
-toidentifier@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
- integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
-
-ts-loader@^8.3.0:
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.3.0.tgz#83360496d6f8004fab35825279132c93412edf33"
- integrity sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==
- dependencies:
- chalk "^4.1.0"
- enhanced-resolve "^4.0.0"
- loader-utils "^2.0.0"
- micromatch "^4.0.0"
- semver "^7.3.4"
-
-tslib@^1.9.0, tslib@^1.9.3:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
-tslib@^2.0.3:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
- integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
-
-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.20.2:
- version "0.20.2"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
- integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-
-type-fest@^0.21.3:
- version "0.21.3"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
- integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
-
-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"
- integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
- dependencies:
- 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.3.5:
- version "4.3.5"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
- integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
-
-uglify-js@^3.6.0:
- version "3.13.10"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d"
- integrity sha512-57H3ACYFXeo1IaZ1w02sfA71wI60MGco/IQFjOqK+WtKoprh7Go2/yvd2HPtoJILO2Or84ncLccI4xoHMTSbGg==
-
-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"
-
-unbox-primitive@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
- integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
- dependencies:
- function-bind "^1.1.1"
- has-bigints "^1.0.1"
- has-symbols "^1.0.2"
- which-boxed-primitive "^1.0.2"
-
-unicode-canonical-property-names-ecmascript@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
- integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
-
-unicode-match-property-ecmascript@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
- integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
- dependencies:
- unicode-canonical-property-names-ecmascript "^1.0.4"
- unicode-property-aliases-ecmascript "^1.0.4"
-
-unicode-match-property-value-ecmascript@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531"
- integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==
-
-unicode-property-aliases-ecmascript@^1.0.4:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
- integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
-
-union-value@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
- integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
- dependencies:
- arr-union "^3.1.0"
- get-value "^2.0.6"
- is-extendable "^0.1.1"
- set-value "^2.0.1"
-
-uniq@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
- integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
-
-uniqs@^2.0.0:
- version "2.0.0"
- 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"
- integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
- dependencies:
- has-value "^0.3.1"
- isobject "^3.0.0"
-
-upath@^1.1.1:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
- integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
-
-uri-js@^4.2.2:
- version "4.4.1"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
- integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
- 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.4.3, url-parse@^1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b"
- integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==
- dependencies:
- querystringify "^2.1.1"
- requires-port "^1.0.0"
-
-url@^0.11.0:
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
- integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
- dependencies:
- punycode "1.3.2"
- querystring "0.2.0"
-
-use@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
- integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
-
-util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
- integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-
-util.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"
- integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
- dependencies:
- inherits "2.0.1"
-
-util@^0.11.0:
- version "0.11.1"
- resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
- integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
- dependencies:
- inherits "2.0.3"
-
-utila@~0.4:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
- integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
-
-utils-merge@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
- integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-
-uuid@^3.3.2, uuid@^3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
-v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
- integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
-
-vary@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
- integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
-
-vendors@^1.0.0:
- 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.2"
- resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
- integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
-
-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:
- loose-envify "^1.2.0"
-
-vue-context@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/vue-context/-/vue-context-6.0.0.tgz#97a1a423fc86fb7a54f46d1567670aba281a5970"
- integrity sha512-x8gO6xgj0MtTCWcYbDjO/7VJ/gT+nV+unqICvsN0hwqqBzft31eYyExYqrSvfDCRI7ixxObu5tbl7BQAKge7eg==
- dependencies:
- vue-clickaway "^2.2.2"
-
-vue-eslint-parser@^7.8.0:
- version "7.8.0"
- resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.8.0.tgz#43850bf856c9a69d62c0e12769609c338423684b"
- integrity sha512-ehmmrLZNYLUoKayvVW8l8HyPQIfuYZHiJoQLRP3dapDlTU7bGs4tqIKVGdAEpMuXS/b4R/PImCt7Tkj4UhX1SQ==
- dependencies:
- debug "^4.1.1"
- eslint-scope "^5.1.1"
- eslint-visitor-keys "^1.1.0"
- espree "^6.2.1"
- esquery "^1.4.0"
- lodash "^4.17.21"
- semver "^6.3.0"
-
-vue-github-button@^1.2.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/vue-github-button/-/vue-github-button-1.3.0.tgz#a4da317a3b93d05824768b3916a0d22fa91603be"
- integrity sha512-Cc92t+GBLwBPhwtHSvKXjbx07U3+6xdi+eR+s9c734tHbndipCLenJjLVkgErNhKZ0EvDjRyuu8Hu69gg9/TxQ==
- 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-gtag@^1.16.1:
- version "1.16.1"
- resolved "https://registry.yarnpkg.com/vue-gtag/-/vue-gtag-1.16.1.tgz#edb2f20ab4f6c4d4d372dfecf8c1fcc8ab890181"
- integrity sha512-5vs0pSGxdqrfXqN1Qwt0ZFXG0iTYjRMu/saddc7QIC5yp+DKgjWQRpGYVa7Pq+KbThxwzzMfo0sGi7ISa6NowA==
-
-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.2:
- version "15.9.7"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.7.tgz#15b05775c3e0c38407679393c2ce6df673b01044"
- integrity sha512-qzlsbLV1HKEMf19IqCJqdNvFJRCI58WNbS6XbPqK13MrLz65es75w392MSQ5TsARAfIjUw+ATm3vlCXUJSOH9Q==
- dependencies:
- "@vue/component-compiler-utils" "^3.1.0"
- hash-sum "^1.0.2"
- loader-utils "^1.1.0"
- vue-hot-reload-api "^2.3.0"
- vue-style-loader "^4.1.0"
-
-vue-material@^1.0.0-beta-15:
- 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:
- 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@^4.1.0, vue-style-loader@^4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35"
- integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==
- dependencies:
- hash-sum "^1.0.2"
- loader-utils "^1.0.2"
-
-vue-template-compiler@^2.6.14:
- version "2.6.14"
- resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763"
- integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==
- dependencies:
- de-indent "^1.0.2"
- he "^1.1.0"
-
-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-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:
- vue "^2.6.10"
-
-vue@^2.6.10, vue@^2.6.14:
- version "2.6.14"
- resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
- integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
-
-vuex@^3.6.2:
- version "3.6.2"
- resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"
- integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==
-
-watchpack-chokidar2@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
- integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==
- dependencies:
- chokidar "^2.1.8"
-
-watchpack@^1.7.4:
- version "1.7.5"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
- integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==
- dependencies:
- graceful-fs "^4.1.2"
- neo-async "^2.5.0"
- optionalDependencies:
- chokidar "^3.4.1"
- watchpack-chokidar2 "^2.0.1"
-
-wbuf@^1.1.0, wbuf@^1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
- integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
- dependencies:
- minimalistic-assert "^1.0.0"
-
-webpack-cli@^4.7.2:
- version "4.7.2"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.2.tgz#a718db600de6d3906a4357e059ae584a89f4c1a5"
- integrity sha512-mEoLmnmOIZQNiRl0ebnjzQ74Hk0iKS5SiEEnpq3dRezoyR3yPaeQZCMCe+db4524pj1Pd5ghZXjT41KLzIhSLw==
- dependencies:
- "@discoveryjs/json-ext" "^0.5.0"
- "@webpack-cli/configtest" "^1.0.4"
- "@webpack-cli/info" "^1.3.0"
- "@webpack-cli/serve" "^1.5.1"
- colorette "^1.2.1"
- commander "^7.0.0"
- execa "^5.0.0"
- fastest-levenshtein "^1.0.12"
- import-local "^3.0.2"
- interpret "^2.2.0"
- rechoir "^0.7.0"
- v8-compile-cache "^2.2.0"
- webpack-merge "^5.7.3"
-
-webpack-dev-middleware@^3.7.2:
- version "3.7.3"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
- integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==
- 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.2:
- version "3.11.2"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708"
- integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==
- dependencies:
- ansi-html "0.0.7"
- bonjour "^3.5.0"
- chokidar "^2.1.8"
- compression "^1.7.4"
- connect-history-api-fallback "^1.6.0"
- debug "^4.1.1"
- del "^4.1.1"
- express "^4.17.1"
- html-entities "^1.3.1"
- http-proxy-middleware "0.19.1"
- import-local "^2.0.0"
- internal-ip "^4.3.0"
- ip "^1.1.5"
- is-absolute-url "^3.0.3"
- killable "^1.0.1"
- loglevel "^1.6.8"
- opn "^5.5.0"
- p-retry "^3.0.1"
- portfinder "^1.0.26"
- schema-utils "^1.0.0"
- selfsigned "^1.10.8"
- semver "^6.3.0"
- serve-index "^1.9.1"
- sockjs "^0.3.21"
- sockjs-client "^1.5.0"
- spdy "^4.0.2"
- strip-ansi "^3.0.1"
- supports-color "^6.1.0"
- url "^0.11.0"
- webpack-dev-middleware "^3.7.2"
- webpack-log "^2.0.0"
- ws "^6.2.1"
- yargs "^13.3.2"
-
-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.2"
- resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-3.0.2.tgz#edf64fe4cabffeb04a03ca44d89f9908a4a9d238"
- integrity sha512-ijm2zgqTY2omtlxRNrtDqxAQOrfAGMxWg9fQB/kuFSeZjx/OkYnfYLqsjf/JkrWOHINMzqxaJDXaog6Mx9KaHg==
- dependencies:
- chalk "^2.4.2"
- loglevelnext "^3.0.1"
- nanoid "^2.0.3"
-
-webpack-merge@^5.7.3, webpack-merge@^5.8.0:
- version "5.8.0"
- resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61"
- integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==
- 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@^4.46.0:
- version "4.46.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542"
- integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
- dependencies:
- "@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.5.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.5.1, websocket-driver@^0.7.4:
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
- integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
- dependencies:
- http-parser-js ">=0.5.1"
- safe-buffer ">=5.1.0"
- websocket-extensions ">=0.1.1"
-
-websocket-extensions@>=0.1.1:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
- integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
-
-whatwg-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-boxed-primitive@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
- integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
- dependencies:
- is-bigint "^1.0.1"
- is-boolean-object "^1.1.0"
- is-number-object "^1.0.4"
- is-string "^1.0.5"
- is-symbol "^1.0.3"
-
-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@^1.2.9:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
- integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
- dependencies:
- isexe "^2.0.0"
-
-which@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
-wildcard@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
- integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
-
-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==
-
-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:
- 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"
-
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- 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=
-
-ws@^6.2.1:
- version "6.2.2"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
- integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
- 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@^4.0.0:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
- integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
-
-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.2:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
- integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
-
-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==
-
-yaml@^1.10.0:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
- integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
-
-yargs-parser@^13.1.2:
- version "13.1.2"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
- integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
- dependencies:
- camelcase "^5.0.0"
- decamelize "^1.2.0"
-
-yargs@^13.3.2:
- version "13.3.2"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
- integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
- dependencies:
- cliui "^5.0.0"
- find-up "^3.0.0"
- get-caller-file "^2.0.1"
- require-directory "^2.1.1"
- require-main-filename "^2.0.0"
- set-blocking "^2.0.0"
- string-width "^3.0.0"
- which-module "^2.0.0"
- y18n "^4.0.0"
- yargs-parser "^13.1.2"
diff --git a/vndk/tools/definition-tool/datasets/vndk-lib-extra-list-34.txt b/vndk/tools/definition-tool/datasets/vndk-lib-extra-list-34.txt
new file mode 100644
index 0000000..0ce0dc2
--- /dev/null
+++ b/vndk/tools/definition-tool/datasets/vndk-lib-extra-list-34.txt
@@ -0,0 +1,46 @@
+FWK-ONLY-RS: libft2.so
+FWK-ONLY-RS: libmediandk.so
+
+LLNDK: libclang_rt.asan-aarch64-android.so
+LLNDK: libclang_rt.asan-arm-android.so
+LLNDK: libclang_rt.asan-i686-android.so
+LLNDK: libclang_rt.asan-mips-android.so
+LLNDK: libclang_rt.asan-mips64-android.so
+LLNDK: libclang_rt.asan-x86_64-android.so
+
+VNDK-core: libclang_rt.scudo-aarch64-android.so
+VNDK-core: libclang_rt.scudo-arm-android.so
+VNDK-core: libclang_rt.scudo-i686-android.so
+VNDK-core: libclang_rt.scudo-x86_64-android.so
+
+VNDK-core: libclang_rt.scudo_minimal-aarch64-android.so
+VNDK-core: libclang_rt.scudo_minimal-arm-android.so
+VNDK-core: libclang_rt.scudo_minimal-i686-android.so
+VNDK-core: libclang_rt.scudo_minimal-x86_64-android.so
+
+VNDK-core: libclang_rt.ubsan_standalone-aarch64-android.so
+VNDK-core: libclang_rt.ubsan_standalone-arm-android.so
+VNDK-core: libclang_rt.ubsan_standalone-i686-android.so
+VNDK-core: libclang_rt.ubsan_standalone-mips-android.so
+VNDK-core: libclang_rt.ubsan_standalone-mips64-android.so
+VNDK-core: libclang_rt.ubsan_standalone-x86_64-android.so
+
+LLNDK-private: ld-android.so
+LLNDK-private: libandroid_runtime_lazy.so
+LLNDK-private: libc_malloc_debug.so
+LLNDK-private: libdl_android.so
+LLNDK-private: libnetd_client.so
+LLNDK-private: libtextclassifier_hash.so
+
+# Same-Process HAL implementations
+SP-HAL: [regex]^.*/android\.hardware\.graphics\.mapper@\d+\.\d+-impl\.so$
+SP-HAL: [regex]^.*/android\.hardware\.renderscript@1\.0-impl\.so$
+SP-HAL: [regex]^.*/gralloc\..*\.so$
+SP-HAL: [regex]^/vendor/.*/libEGL_.*\.so$
+SP-HAL: [regex]^/vendor/.*/libGLES_.*\.so$
+SP-HAL: [regex]^/vendor/.*/libGLESv1_CM_.*\.so$
+SP-HAL: [regex]^/vendor/.*/libGLESv2_.*\.so$
+SP-HAL: [regex]^/vendor/.*/libGLESv3_.*\.so$
+SP-HAL: [regex]^/vendor/.*/libPVRRS\.so$
+SP-HAL: [regex]^/vendor/.*/libRSDriver.*\.so$
+SP-HAL: [regex]^/vendor/.*/vulkan.*\.so$