diff --git a/.gitignore b/.gitignore
index ab6b7b48..5a0eff78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,16 @@
*.idea/*
-*/build/*
+build/
.gradle/*
*/.gradle/*
local.properties
*.iml
*.gpg
-*.DS_STORE
\ No newline at end of file
+*.DS_STORE
+
+# IntelliJ Code Styles
+!/.idea/codeStyles/
+
+# moko-resources-generated
+moko-resources-generated.js
+# generated
+pack-test-resources-generated.js
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..1bec35e5
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8b39c06b..67bf7c7c 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
# Kamel
-[![Version](https://img.shields.io/maven-central/v/com.alialbaali.kamel/kamel-core?label=version&color=blue)](https://search.maven.org/search?q=com.alialbaali.kamel)
-[![Snapshot](https://img.shields.io/nexus/s/com.alialbaali.kamel/kamel-core?label=snapshot&server=https%3A%2F%2Foss.sonatype.org)](https://oss.sonatype.org/content/repositories/snapshots/com/alialbaali/kamel/)
+[![Version](https://img.shields.io/maven-central/v/media.kamel/kamel-core?label=version&color=blue)](https://search.maven.org/search?q=media.kamel)
+[![Snapshot](https://img.shields.io/nexus/s/media.kamel/kamel-core?label=snapshot&server=https%3A%2F%2Fs01.oss.sonatype.org)](https://s01.oss.sonatype.org/content/repositories/snapshots/media/kamel/)
[![License](https://img.shields.io/github/license/alialbaali/kamel)](http://www.apache.org/licenses/LICENSE-2.0)
-[![Kotlin](https://img.shields.io/badge/kotlin-v1.7.0-blue.svg?logo=kotlin)](http://kotlinlang.org)
-[![Compose](https://img.shields.io/badge/compose-v1.2.0-alpha2?logo=compose&color=blue)](http://kotlinlang.org)
+[![Kotlin](https://img.shields.io/badge/kotlin-v1.8.20-blue.svg?logo=kotlin)](http://kotlinlang.org)
+[![Compose Multiplatform](https://img.shields.io/badge/Compose%20Multiplatform-v1.4.0-blue)](https://github.com/JetBrains/compose-multiplatform)
-Kamel is an asynchronous media loading library for Compose. It provides a simple, customizable and
+Kamel is an asynchronous media loading library for [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform). It provides a simple, customizable and
efficient way to load, cache, decode and display images in your application. By default, it uses
Ktor client for loading resources.
@@ -49,7 +49,7 @@ kotlin {
sourceSets {
commonMain {
dependencies {
- implementation("com.alialbaali.kamel:kamel-image:0.4.0")
+ implementation("media.kamel:kamel-image:0.5.0")
// ...
}
}
@@ -63,7 +63,7 @@ Add the dependency to the dependencies block:
```kotlin
dependencies {
- implementation("com.alialbaali.kamel:kamel-image:0.4.0")
+ implementation("media.kamel:kamel-image:0.5.0")
// ...
}
```
@@ -90,9 +90,12 @@ lazyPainterResource(data = Url("https://www.example.com/image.jpg"))
// URI
lazyPainterResource(data = URI("https://www.example.com/image.png"))
-// File
+// File (JVM, Native)
lazyPainterResource(data = File("/path/to/image.png"))
+// File (JS)
+lazyPainterResource(data = File(org.w3c.files.File(arrayOf(blob), "/path/to/image.png")))
+
// URL
lazyPainterResource(data = URL("https://www.example.com/image.jpg"))
diff --git a/build.gradle.kts b/build.gradle.kts
index 24ded7a6..2c707198 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,9 +9,8 @@ buildscript {
repositories {
google()
mavenCentral()
+ gradlePluginPortal()
maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev")
- maven(url = "https://dl.bintray.com/kotlin/dokka")
- maven(url = "https://kotlin.bintray.com/ktor")
}
dependencies {
@@ -57,8 +56,6 @@ allprojects {
google()
mavenCentral()
maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev")
- maven(url = "https://dl.bintray.com/kotlin/dokka")
- maven(url = "https://kotlin.bintray.com/ktor")
}
val emptyJavadocJar by tasks.registering(Jar::class) {
@@ -107,14 +104,14 @@ allprojects {
name = "MavenCentral"
- val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
- val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
+ val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
+ val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
url = if (version.toString().endsWith("SNAPSHOT")) uri(snapshotsRepoUrl) else uri(releasesRepoUrl)
credentials {
- username = rootProject.ext["ossrh.username"] as String
- password = rootProject.ext["ossrh.password"] as String
+ username = rootProject.ext["ossrh.username"] as String? ?: ""
+ password = rootProject.ext["ossrh.password"] as String? ?: ""
}
}
@@ -126,7 +123,8 @@ allprojects {
tasks.withType {
kotlinOptions {
- freeCompilerArgs = listOf("-Xallow-result-return-type", "-Xopt-in=kotlin.RequiresOptIn")
+ freeCompilerArgs = listOf("-Xallow-result-return-type")
+ jvmTarget = "11"
}
}
@@ -134,7 +132,7 @@ allprojects {
nexusStaging {
packageGroup = Kamel.Group
- stagingProfileId = rootProject.ext["stagingProfileId"] as String
- username = rootProject.ext["ossrh.username"] as String
- password = rootProject.ext["ossrh.password"] as String
+ stagingProfileId = rootProject.ext["stagingProfileId"] as String? ?: ""
+ username = rootProject.ext["ossrh.username"] as String? ?: ""
+ password = rootProject.ext["ossrh.password"] as String? ?: ""
}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 445e21f7..f1552441 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -1,5 +1,5 @@
repositories {
- jcenter()
+ mavenCentral()
}
plugins {
diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt
index 357f8428..afaa0a78 100644
--- a/buildSrc/src/main/kotlin/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/Dependencies.kt
@@ -5,41 +5,59 @@ object Dependencies {
const val KotlinReflect = "org.jetbrains.kotlin:kotlin-reflect:${Versions.Kotlin}"
object Android {
- const val Appcompat = "androidx.appcompat:appcompat:${Versions.Android.Appcompat}"
- const val Core = "androidx.core:core-ktx:${Versions.Android.Core}"
const val ActivityCompose = "androidx.activity:activity-compose:${Versions.Android.ActivityCompose}"
+ const val Appcompat = "androidx.appcompat:appcompat:${Versions.Android.Appcompat}"
const val GradlePlugin = "com.android.tools.build:gradle:${Versions.AGP}"
const val Material = "com.google.android.material:material:${Versions.Android.Material}"
+ const val Annotation = "androidx.annotation:annotation:${Versions.Android.Annotation}"
}
object Ktor {
const val Core = "io.ktor:ktor-client-core:${Versions.Ktor}"
- const val Logging = "io.ktor:ktor-client-logging:${Versions.Ktor}"
const val Android = "io.ktor:ktor-client-android:${Versions.Ktor}"
+ const val Darwin = "io.ktor:ktor-client-darwin:${Versions.Ktor}"
+ const val Js = "io.ktor:ktor-client-js:${Versions.Ktor}"
const val CIO = "io.ktor:ktor-client-cio:${Versions.Ktor}"
}
+ object Coroutines {
+ const val Core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Coroutines}"
+ const val Test = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Coroutines}"
+ }
+
object Testing {
const val Ktor = "io.ktor:ktor-client-mock:${Versions.Ktor}"
- const val Coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Coroutines}"
const val Compose = "org.jetbrains.compose.ui:ui-test-junit4:${Versions.Compose}"
+ const val MokoResources = "dev.icerock.moko:resources:${Versions.MokoResources}"
+ }
+
+ object MokoResources {
+ const val Core = "dev.icerock.moko:resources:${Versions.MokoResources}"
+ const val Test = "dev.icerock.moko:resources-test:${Versions.MokoResources}"
+ }
+
+ object XmlUtil {
+ const val Serialization = "io.github.pdvrieze.xmlutil:serialization:${Versions.XmlUtil}"
}
}
-private object Versions {
+object Versions {
+
+ const val Kotlin = "1.8.20"
+ const val Ktor = "2.3.0"
+ const val Coroutines = "1.6.4"
+ const val Compose = "1.4.0"
+ const val AGP = "7.4.2"
+ const val MokoResources = "0.22.0"
+ const val XmlUtil = "0.86.0"
- const val Kotlin = "1.7.0"
- const val Ktor = "2.0.3"
- const val Coroutines = "1.6.3"
- const val Compose = "1.2.0-alpha01-dev753"
- const val AGP = "7.2.1"
object Android {
- const val Appcompat = "1.4.2"
- const val Core = "1.8.0"
- const val ActivityCompose = "1.5.0"
- const val Material = "1.6.1"
+ const val ActivityCompose = "1.7.1"
+ const val Appcompat = "1.6.1"
+ const val Material = "1.8.0"
+ const val Annotation = "1.6.0"
}
}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/Kamel.kt b/buildSrc/src/main/kotlin/Kamel.kt
index 3b3693d1..5ddd45ea 100644
--- a/buildSrc/src/main/kotlin/Kamel.kt
+++ b/buildSrc/src/main/kotlin/Kamel.kt
@@ -1,4 +1,4 @@
object Kamel {
- const val Group = "com.alialbaali.kamel"
- const val Version = "0.4.1"
+ const val Group = "media.kamel"
+ const val Version = "0.5.0-SNAPSHOT"
}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/Plugins.kt b/buildSrc/src/main/kotlin/Plugins.kt
index 5e9b8fdb..b95322ea 100644
--- a/buildSrc/src/main/kotlin/Plugins.kt
+++ b/buildSrc/src/main/kotlin/Plugins.kt
@@ -9,11 +9,11 @@ inline val PluginDependenciesSpec.compose: PluginDependencySpec
inline val PluginDependenciesSpec.multiplatform: PluginDependencySpec
get() = kotlin("multiplatform")
-inline val PluginDependenciesSpec.`nexus-staging`: PluginDependencySpec
- get() = id("io.codearte.nexus-staging") version "0.22.0"
+inline val PluginDependenciesSpec.mokoResources: PluginDependencySpec
+ get() = id("dev.icerock.mobile.multiplatform-resources") version Versions.MokoResources
-inline val PluginDependenciesSpec.dokka: PluginDependencySpec
- get() = id("org.jetbrains.dokka") version "1.4.20"
+inline val PluginDependenciesSpec.`nexus-staging`: PluginDependencySpec
+ get() = id("io.codearte.nexus-staging") version "0.30.0"
inline val PluginDependenciesSpec.`android-library`: PluginDependencySpec
get() = id("com.android.library")
diff --git a/buildSrc/src/main/kotlin/Targets.kt b/buildSrc/src/main/kotlin/Targets.kt
new file mode 100644
index 00000000..bb0a4a7c
--- /dev/null
+++ b/buildSrc/src/main/kotlin/Targets.kt
@@ -0,0 +1,14 @@
+object Targets {
+
+ val iosTargets = arrayOf(
+ "iosArm64", "iosX64", "iosSimulatorArm64",
+ )
+ val macosTargets = arrayOf(
+ "macosX64", "macosArm64",
+ )
+ val darwinTargets = iosTargets + macosTargets
+ val linuxTargets = arrayOf()
+ val mingwTargets = arrayOf()
+ val nativeTargets = linuxTargets + darwinTargets + mingwTargets
+
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index b8822f8b..cefec6eb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,15 @@
+org.gradle.jvmargs=-Xmx3g
kotlin.code.style=official
+kotlin.native.cacheKind=none
+kotlin.native.useEmbeddableCompilerJar=true
+kotlin.mpp.enableCInteropCommonization=true
+kotlin.mpp.androidSourceSetLayoutVersion=2
+compose.desktop.verbose=true
+
android.useAndroidX=true
-kotlin.mpp.stability.nowarn=true
\ No newline at end of file
+android.disableAutomaticComponentCreation=true
+kotlin.mpp.stability.nowarn=true
+
+org.jetbrains.compose.experimental.jscanvas.enabled=true
+org.jetbrains.compose.experimental.macos.enabled=true
+org.jetbrains.compose.experimental.uikit.enabled=true
\ No newline at end of file
diff --git a/gradle/pack-core-tests-resources.gradle.kts b/gradle/pack-core-tests-resources.gradle.kts
new file mode 100644
index 00000000..e3ab88bf
--- /dev/null
+++ b/gradle/pack-core-tests-resources.gradle.kts
@@ -0,0 +1,36 @@
+/***
+ * How to handle Web Workers "standard" syntax with webpack:
+ * https://stackoverflow.com/a/41630622/1363742
+ */
+fun createWebpackConfig(){
+ val rootProjectAbsPath = rootProject.projectDir.absolutePath
+ val path = """"$rootProjectAbsPath/kamel-core/build/generated/moko/jsMain/iokamelcore/res""""
+ val webpackConfig = File(projectDir, "webpack.config.d/pack-test-resources-generated.js")
+ val configText =
+ """const path = require('path');
+
+const mokoResourcePath = path.resolve($path);
+
+config.module.rules.push(
+ {
+ test: /\.(.*)/,
+ include: [
+ path.resolve(mokoResourcePath)
+ ],
+ type: 'asset/resource'
+ }
+);
+
+config.resolve.modules.push(
+ path.resolve(mokoResourcePath)
+);"""
+ webpackConfig.writeText(configText)
+}
+
+tasks.create("createPackResourcesWebpackConfig") {
+ doFirst {
+ createWebpackConfig()
+ }
+}
+
+tasks.getByName("jsJar").dependsOn("createPackResourcesWebpackConfig")
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c0..249e5832 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1565a526..fae08049 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Sun Jul 10 06:06:14 TRT 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
\ No newline at end of file
diff --git a/gradlew b/gradlew
index 4f906e0c..a69d9cb6 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,101 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=`expr $i + 1`
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index ac1b06f9..53a6b238 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/kamel-core/build.gradle.kts b/kamel-core/build.gradle.kts
index b461c4d6..c14b4b5f 100644
--- a/kamel-core/build.gradle.kts
+++ b/kamel-core/build.gradle.kts
@@ -1,5 +1,10 @@
-import org.jetbrains.compose.compose
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
+import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractExecutable
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary
+import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
+import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
+import java.io.FileFilter
plugins {
multiplatform
@@ -13,14 +18,48 @@ kotlin {
explicitApi = ExplicitApiMode.Warning
jvm()
+ js(IR) {
+ browser()
+ }
+ for (target in Targets.macosTargets) {
+ targets.add(
+ (presets.getByName(target).createTarget(target) as KotlinNativeTarget).apply {
+ binaries.forEach {
+ it.apply {
+ freeCompilerArgs += listOf(
+ "-linker-option", "-framework", "-linker-option", "Metal"
+ )
+ }
+ }
+ }
+ )
+ }
+ for (target in Targets.iosTargets) {
+ targets.add(
+ (presets.getByName(target).createTarget(target) as KotlinNativeTarget).apply {
+ binaries.forEach {
+ it.apply {
+ freeCompilerArgs += listOf(
+ "-linker-option", "-framework", "-linker-option", "Metal",
+ "-linker-option", "-framework", "-linker-option", "CoreText",
+ "-linker-option", "-framework", "-linker-option", "CoreGraphics"
+ )
+ }
+ }
+ }
+ )
+ }
+
+
sourceSets {
val commonMain by getting {
dependencies {
- api(compose.ui)
- api(compose.foundation)
- api(compose.runtime)
+ implementation(compose.ui)
+ implementation(compose.foundation)
+ implementation(compose.runtime)
+ implementation(Dependencies.Coroutines.Core)
api(Dependencies.Ktor.Core)
}
}
@@ -28,23 +67,48 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(project(":kamel-tests"))
- implementation(kotlin("test-common"))
- implementation(kotlin("test-annotations-common"))
+ implementation(kotlin("test"))
implementation(Dependencies.Testing.Ktor)
- implementation(Dependencies.Testing.Coroutines)
- implementation(Dependencies.Testing.Compose)
+ implementation(Dependencies.Coroutines.Test)
}
}
val jvmMain by getting {
+ dependsOn(commonMain)
dependencies {
implementation(Dependencies.KotlinReflect)
}
}
val jvmTest by getting {
- dependencies {
- implementation(kotlin("test-junit"))
+ }
+
+ val nonJvmMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val nonJvmTest by creating {
+ dependsOn(commonTest)
+ }
+
+ val jsMain by getting {
+ dependsOn(nonJvmMain)
+ }
+
+ val darwinMain by creating {
+ dependsOn(nonJvmMain)
+ }
+
+ val darwinTest by creating {
+ dependsOn(nonJvmTest)
+ }
+
+ Targets.darwinTargets.forEach { target ->
+ getByName("${target}Main") {
+ dependsOn(darwinMain)
+ }
+ getByName("${target}Test") {
+ dependsOn(darwinTest)
}
}
@@ -54,13 +118,66 @@ kotlin {
}
}
- targets.all {
- compilations.all {
- kotlinOptions {
- freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn")
+ }
+}
+
+// https://youtrack.jetbrains.com/issue/KT-46466
+val dependsOnTasks = mutableListOf()
+tasks.withType().configureEach {
+ dependsOnTasks.add(this.name.replace("publish", "sign").replaceAfter("Publication", ""))
+ dependsOn(dependsOnTasks)
+}
+
+
+// todo: Remove when resolved: https://github.com/icerockdev/moko-resources/issues/372
+tasks.withType()
+ .matching { linkTask ->
+ linkTask.binary is AbstractExecutable
+ }
+ .configureEach {
+ val task: KotlinNativeLink = this
+
+ this.doLast {
+ val binary: NativeBinary = task.binary
+ val outputDir: File = task.outputFile.get().parentFile
+ task.libraries
+ .filter { library -> library.extension == "klib" }
+ .filter(File::exists)
+ .forEach { inputFile ->
+ val klibKonan = org.jetbrains.kotlin.konan.file.File(inputFile.path)
+ val klib = KotlinLibraryLayoutImpl(
+ klib = klibKonan,
+ component = "default"
+ )
+ val layout = klib.extractingToTemp
+
+ // extracting bundles
+ layout
+ .resourcesDir
+ .absolutePath
+ .let(::File)
+ .listFiles(FileFilter { it.extension == "bundle" })
+ // copying bundles to app
+ ?.forEach { bundleFile ->
+ logger.info("${bundleFile.absolutePath} copying to $outputDir")
+ bundleFile.copyRecursively(
+ target = File(outputDir, bundleFile.name),
+ overwrite = true
+ )
+ }
}
- }
}
-
}
+
+
+// todo: remove after https://github.com/icerockdev/moko-resources/issues/392 resolved
+// copy resources from kamel-tests into the proper directory for kamel-samples so they are packaged for
+// the web app
+tasks.register("jsCopyResourcesFromKamelTests") {
+ from("../kamel-tests/build/generated/moko/jsMain/iokameltests/res")
+ into("build/generated/moko/jsMain/iokamelcore/res")
+ dependsOn(":kamel-tests:generateMRjsMain")
}
+tasks.getByName("jsProcessResources").dependsOn("jsCopyResourcesFromKamelTests")
+
+apply(from = "$rootDir/gradle/pack-core-tests-resources.gradle.kts")
\ No newline at end of file
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/ImageLoading.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/ImageLoading.kt
index e7a877ee..3ac12bf6 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/ImageLoading.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/ImageLoading.kt
@@ -13,6 +13,7 @@ import io.kamel.core.utils.findDecoderFor
import io.kamel.core.utils.findFetcherFor
import io.kamel.core.utils.mapInput
import kotlinx.coroutines.flow.*
+import kotlin.reflect.KClass
/**
* Loads an [ImageBitmap]. This includes mapping, fetching, decoding and caching the image resource.
@@ -21,10 +22,11 @@ import kotlinx.coroutines.flow.*
* @see Mapper
* @see Cache
*/
-public fun KamelConfig.loadImageBitmapResource(
- data: Any,
- resourceConfig: ResourceConfig
-): Flow> = loadResource(data, resourceConfig, imageBitmapCache)
+public fun KamelConfig.loadImageBitmapResource(
+ data: I,
+ resourceConfig: ResourceConfig,
+ dataKClass: KClass<*> = data::class,
+): Flow> = loadResource(data, dataKClass, resourceConfig, imageBitmapCache)
/**
* Loads an [ImageVector]. This includes mapping, fetching, decoding and caching the image resource.
@@ -35,8 +37,9 @@ public fun KamelConfig.loadImageBitmapResource(
*/
public fun KamelConfig.loadImageVectorResource(
data: Any,
- resourceConfig: ResourceConfig
-): Flow> = loadResource(data, resourceConfig, imageVectorCache)
+ resourceConfig: ResourceConfig,
+ dataKClass: KClass<*> = data::class
+): Flow> = loadResource(data, dataKClass, resourceConfig, imageVectorCache)
/**
* Loads SVG [Painter]. This includes mapping, fetching, decoding and caching the image resource.
@@ -47,15 +50,17 @@ public fun KamelConfig.loadImageVectorResource(
*/
public fun KamelConfig.loadSvgResource(
data: Any,
- resourceConfig: ResourceConfig
-): Flow> = loadResource(data, resourceConfig, svgCache)
+ resourceConfig: ResourceConfig,
+ dataKClass: KClass<*> = data::class
+): Flow> = loadResource(data, dataKClass, resourceConfig, svgCache)
private inline fun KamelConfig.loadResource(
data: Any,
+ dataKClass: KClass<*>,
resourceConfig: ResourceConfig,
cache: Cache,
): Flow> = flow {
- val output = mapInput(data)
+ val output = mapInput(data, dataKClass)
val cachedData = cache[output]
if (cachedData != null) {
val resource = Resource.Success(cachedData, DataSource.Memory)
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfig.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfig.kt
index 803a229a..fed090ee 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfig.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfig.kt
@@ -7,6 +7,7 @@ import io.kamel.core.cache.Cache
import io.kamel.core.decoder.Decoder
import io.kamel.core.fetcher.Fetcher
import io.kamel.core.mapper.Mapper
+import kotlin.reflect.KClass
public const val DefaultCacheSize: Int = 100
@@ -20,7 +21,7 @@ public interface KamelConfig {
public val decoders: List>
- public val mappers: List>
+ public val mappers: Map, Mapper>
/**
* Number of entries to cache. Default is 100.
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfigBuilder.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfigBuilder.kt
index 8ba00f96..fff29124 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfigBuilder.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/config/KamelConfigBuilder.kt
@@ -15,10 +15,13 @@ import io.kamel.core.mapper.Mapper
import io.kamel.core.mapper.StringMapper
import io.kamel.core.mapper.URIMapper
import io.kamel.core.mapper.URLMapper
+import io.kamel.core.utils.URI
+import io.kamel.core.utils.URL
import io.ktor.client.*
import io.ktor.client.engine.*
import io.ktor.client.request.*
import io.ktor.http.*
+import kotlin.reflect.KClass
public class KamelConfigBuilder {
@@ -26,7 +29,7 @@ public class KamelConfigBuilder {
internal val decoders: MutableList> = mutableListOf()
- internal val mappers: MutableList> = mutableListOf()
+ internal val mappers: MutableMap, Mapper> = mutableMapOf()
public var imageBitmapCacheSize: Int = 0
@@ -43,7 +46,7 @@ public class KamelConfigBuilder {
}
public fun mapper(mapper: Mapper) {
- mappers += mapper as Mapper
+ mappers[mapper.inputKClass] = mapper as Mapper
}
public fun build(): KamelConfig = object : KamelConfig {
@@ -52,7 +55,7 @@ public class KamelConfigBuilder {
override val decoders: List> = this@KamelConfigBuilder.decoders
- override val mappers: List> = this@KamelConfigBuilder.mappers
+ override val mappers: Map, Mapper> = this@KamelConfigBuilder.mappers
override val imageBitmapCache: Cache = LruCache(imageBitmapCacheSize)
@@ -120,7 +123,7 @@ public fun KamelConfigBuilder.takeFrom(config: KamelConfig): KamelConfigBuilder
svgCacheSize = config.svgCache.maxSize
config.fetchers.forEach { fetcher(it) }
config.decoders.forEach { decoder(it) }
- config.mappers.forEach { mapper(it) }
+ config.mappers.values.forEach { mapper(it) }
return this
}
\ No newline at end of file
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/config/ResourceConfigBuilder.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/config/ResourceConfigBuilder.kt
index 112e21f7..64d1b986 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/config/ResourceConfigBuilder.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/config/ResourceConfigBuilder.kt
@@ -1,11 +1,10 @@
package io.kamel.core.config
-import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.unit.Density
-import io.kamel.core.utils.IO
+import io.kamel.core.utils.Kamel
+import io.kamel.core.utils.supervisorJob
import io.ktor.client.request.*
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
public class ResourceConfigBuilder {
@@ -20,7 +19,7 @@ public class ResourceConfigBuilder {
* CoroutineContext used while loading the resource.
* @see ResourceConfig.coroutineContext
*/
- public var coroutineContext: CoroutineContext = Job() + Dispatchers.IO
+ public var coroutineContext: CoroutineContext = supervisorJob.plus(Dispatchers.Kamel)
/**
* Screen density.
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/decoder/Decoder.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/decoder/Decoder.kt
index fece079a..e1f6490f 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/decoder/Decoder.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/decoder/Decoder.kt
@@ -2,11 +2,17 @@ package io.kamel.core.decoder
import io.kamel.core.config.ResourceConfig
import io.ktor.utils.io.*
+import kotlin.reflect.KClass
/**
* Decodes [ByteReadChannel] to [T].
*/
-public interface Decoder {
+public interface Decoder {
+
+ /**
+ * The KClass of the output of this decoder
+ */
+ public val outputKClass: KClass
/**
* Decodes [channel] to [T].
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/Fetcher.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/Fetcher.kt
index 80a71252..bebdb8c4 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/Fetcher.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/Fetcher.kt
@@ -5,12 +5,18 @@ import io.kamel.core.Resource
import io.kamel.core.config.ResourceConfig
import io.ktor.utils.io.*
import kotlinx.coroutines.flow.Flow
+import kotlin.reflect.KClass
/**
* Fetches and transfers data into a [ByteReadChannel] asynchronously.
*/
public interface Fetcher {
+ /**
+ * The KClass type for which this fetcher supports as a data input
+ */
+ public val inputDataKClass: KClass
+
/**
* Source from where data has been loaded.
*/
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/HttpFetcher.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/HttpFetcher.kt
index 7b6076b9..1045b57f 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/HttpFetcher.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/fetcher/HttpFetcher.kt
@@ -11,12 +11,15 @@ import io.ktor.http.*
import io.ktor.utils.io.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
+import kotlin.reflect.KClass
/**
* Fetcher that fetches [ByteReadChannel] from network using [Url].
*/
internal class HttpFetcher(private val client: HttpClient) : Fetcher {
+ override val inputDataKClass: KClass = Url::class
+
override val source: DataSource = DataSource.Network
override val Url.isSupported: Boolean
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mapper.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mapper.kt
index bdccb32f..c426eff4 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mapper.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mapper.kt
@@ -1,12 +1,17 @@
package io.kamel.core.mapper
+import kotlin.reflect.KClass
+
/**
* Mapper used to map input [I] to output [O].
* @see StringMapper
* @see URLMapper
* @see URIMapper
*/
-public fun interface Mapper {
+public interface Mapper {
+
+ public val inputKClass: KClass
+ public val outputKClass: KClass
/**
* Maps input [I] to output [O].
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mappers.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mappers.kt
index 4da863c1..2f8d94b8 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mappers.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/mapper/Mappers.kt
@@ -3,8 +3,17 @@ package io.kamel.core.mapper
import io.kamel.core.utils.URI
import io.kamel.core.utils.URL
import io.ktor.http.*
+import kotlin.reflect.KClass
-internal val StringMapper: Mapper = Mapper { Url(it) }
+internal val StringMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = String::class
+ override val outputKClass: KClass
+ get() = Url::class
+
+ override fun map(input: String): Url = Url(input)
+
+}
internal expect val URLMapper: Mapper
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/ConfigUtils.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/ConfigUtils.kt
index 22b5ee70..c358df49 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/ConfigUtils.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/ConfigUtils.kt
@@ -3,22 +3,10 @@ package io.kamel.core.utils
import io.kamel.core.config.KamelConfig
import io.kamel.core.decoder.Decoder
import io.kamel.core.fetcher.Fetcher
+import kotlin.reflect.KClass
-internal fun KamelConfig.mapInput(input: Any): Any {
-
- var output: Any? = null
-
- mappers.findLast {
-
- output = try {
- it.map(input)
- } catch (e: Throwable) {
- null
- }
-
- output != null
- }
-
+internal fun KamelConfig.mapInput(input: Any, inputKClass: KClass<*>): Any {
+ val output = mappers[inputKClass]?.map(input)
return output ?: input
}
diff --git a/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/Platform.kt b/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/Platform.kt
index cc274657..23bc72e3 100644
--- a/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/Platform.kt
+++ b/kamel-core/src/commonMain/kotlin/io/kamel/core/utils/Platform.kt
@@ -2,8 +2,11 @@ package io.kamel.core.utils
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
-internal expect val Dispatchers.IO: CoroutineDispatcher
+internal val supervisorJob = SupervisorJob()
+
+internal expect val Dispatchers.Kamel: CoroutineDispatcher
public expect class File
diff --git a/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigBuilderTest.kt b/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigBuilderTest.kt
index 095abfb1..a50e4650 100644
--- a/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigBuilderTest.kt
+++ b/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigBuilderTest.kt
@@ -7,6 +7,8 @@ import io.kamel.core.mapper.Mapper
import io.kamel.core.mapper.StringMapper
import io.kamel.core.mapper.URIMapper
import io.kamel.core.mapper.URLMapper
+import io.kamel.core.utils.URI
+import io.kamel.core.utils.URL
import io.kamel.tests.HttpMockEngine
import kotlin.test.BeforeTest
import kotlin.test.Test
@@ -43,7 +45,8 @@ class KamelConfigBuilderTest {
builder.stringMapper()
assertTrue { builder.mappers.size == 1 }
- assertTrue { builder.mappers.contains>(StringMapper) }
+ assertTrue { builder.mappers.keys.contains(String::class) }
+ assertTrue { builder.mappers.values.contains>(StringMapper) }
}
@Test
@@ -51,7 +54,8 @@ class KamelConfigBuilderTest {
builder.urlMapper()
assertTrue { builder.mappers.size == 1 }
- assertTrue { builder.mappers.contains>(URLMapper) }
+ assertTrue { builder.mappers.keys.contains(URL::class) }
+ assertTrue { builder.mappers.values.contains>(URLMapper) }
}
@Test
@@ -59,7 +63,8 @@ class KamelConfigBuilderTest {
builder.uriMapper()
assertTrue { builder.mappers.size == 1 }
- assertTrue { builder.mappers.contains>(URIMapper) }
+ assertTrue { builder.mappers.keys.contains(URI::class) }
+ assertTrue { builder.mappers.values.contains>(URIMapper) }
}
@Test
@@ -83,8 +88,10 @@ class KamelConfigBuilderTest {
builder.takeFrom(configBuilder)
assertTrue { builder.fetchers.contains>(FileFetcher) }
- assertTrue { builder.mappers.contains>(URIMapper) }
- assertTrue { builder.mappers.contains>(StringMapper) }
+ assertTrue { builder.mappers.keys.contains(URI::class) }
+ assertTrue { builder.mappers.values.contains>(URIMapper) }
+ assertTrue { builder.mappers.keys.contains(String::class) }
+ assertTrue { builder.mappers.values.contains>(StringMapper) }
assertEquals(100, builder.build().imageBitmapCache.maxSize)
}
@@ -99,8 +106,10 @@ class KamelConfigBuilderTest {
builder.takeFrom(configBuilder.build())
assertTrue { builder.fetchers.contains>(FileFetcher) }
- assertTrue { builder.mappers.contains>(URIMapper) }
- assertTrue { builder.mappers.contains>(StringMapper) }
+ assertTrue { builder.mappers.keys.contains(URI::class) }
+ assertTrue { builder.mappers.values.contains>(URIMapper) }
+ assertTrue { builder.mappers.keys.contains(String::class) }
+ assertTrue { builder.mappers.values.contains>(StringMapper) }
assertEquals(100, builder.build().imageBitmapCache.maxSize)
}
diff --git a/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigUtilsTest.kt b/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigUtilsTest.kt
index c089ff50..8efa2bc0 100644
--- a/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigUtilsTest.kt
+++ b/kamel-core/src/commonTest/kotlin/io/kamel/core/config/KamelConfigUtilsTest.kt
@@ -9,6 +9,7 @@ import io.kamel.tests.HttpMockEngine
import io.kamel.tests.TestStringUrl
import io.ktor.http.*
import io.ktor.utils.io.*
+import kotlin.reflect.KClass
import kotlin.test.Test
import kotlin.test.assertFails
import kotlin.test.assertTrue
@@ -26,21 +27,21 @@ class KamelConfigUtilsTest {
@Test
fun testMapStringInput() {
- val result = config.mapInput(TestStringUrl)
+ val result = config.mapInput(TestStringUrl, String::class)
assertTrue(result is Url)
}
@Test
fun testMapURLInput() {
- val result = config.mapInput(createURL(TestStringUrl))
+ val result = config.mapInput(createURL(TestStringUrl), URL::class)
assertTrue(result is Url)
}
@Test
fun testMapURIInput() {
- val result = config.mapInput(createURI(TestStringUrl))
+ val result = config.mapInput(createURI(TestStringUrl), URI::class)
assertTrue(result is Url)
}
@@ -79,6 +80,9 @@ class KamelConfigUtilsTest {
fun KamelConfigBuilder.fakeImageBitmapDecoder() = decoder(FakeImageBitmapDecoder)
private object FakeImageBitmapDecoder : Decoder {
+
+ override val outputKClass: KClass = ImageBitmap::class
+
override suspend fun decode(channel: ByteReadChannel, resourceConfig: ResourceConfig): ImageBitmap {
return ImageBitmap(1, 1)
}
diff --git a/kamel-core/src/jvmTest/kotlin/io/kamel/core/fetcher/HttpFetcherTest.kt b/kamel-core/src/commonTest/kotlin/io/kamel/core/fetcher/HttpFetcherTest.kt
similarity index 94%
rename from kamel-core/src/jvmTest/kotlin/io/kamel/core/fetcher/HttpFetcherTest.kt
rename to kamel-core/src/commonTest/kotlin/io/kamel/core/fetcher/HttpFetcherTest.kt
index 333f1040..e8a84541 100644
--- a/kamel-core/src/jvmTest/kotlin/io/kamel/core/fetcher/HttpFetcherTest.kt
+++ b/kamel-core/src/commonTest/kotlin/io/kamel/core/fetcher/HttpFetcherTest.kt
@@ -48,7 +48,7 @@ class HttpFetcherTest {
}
@Test
- fun testFetchingEmptyImageBytes(): Unit = runTest {
+ fun testFetchingEmptyImageBytes() = runTest {
val url = Url("/emptyImage.jpg")
val resource = fetcher.fetch(url, resourceConfig)
.first { !it.isLoading }
@@ -59,7 +59,7 @@ class HttpFetcherTest {
}
@Test
- fun testFetchingNonEmptyImageBytes(): Unit = runTest {
+ fun testFetchingNonEmptyImageBytes() = runTest {
val url = Url("/image.svg")
val resource = fetcher.fetch(url, resourceConfig)
.first { !it.isLoading }
diff --git a/kamel-core/src/darwinMain/kotlin/io/kamel/core/fetcher/FileFetcher.kt b/kamel-core/src/darwinMain/kotlin/io/kamel/core/fetcher/FileFetcher.kt
new file mode 100644
index 00000000..67b964a2
--- /dev/null
+++ b/kamel-core/src/darwinMain/kotlin/io/kamel/core/fetcher/FileFetcher.kt
@@ -0,0 +1,32 @@
+package io.kamel.core.fetcher
+
+import io.kamel.core.DataSource
+import io.kamel.core.Resource
+import io.kamel.core.config.ResourceConfig
+import io.kamel.core.utils.File
+import io.ktor.utils.io.*
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import kotlin.reflect.KClass
+
+/**
+ * Fetcher that fetchers [ByteReadChannel] from a file.
+ */
+internal actual object FileFetcher : Fetcher {
+
+ override val inputDataKClass: KClass = File::class
+
+ override val source: DataSource = DataSource.Disk
+
+ override val File.isSupported: Boolean
+ get() = true
+
+ override fun fetch(
+ data: File,
+ resourceConfig: ResourceConfig
+ ): Flow> = flow {
+ val byteReadChannel = ByteReadChannel(data.availableData)
+ emit(Resource.Success(byteReadChannel, source))
+ }
+
+}
\ No newline at end of file
diff --git a/kamel-core/src/darwinMain/kotlin/io/kamel/core/mapper/Mappers.kt b/kamel-core/src/darwinMain/kotlin/io/kamel/core/mapper/Mappers.kt
new file mode 100644
index 00000000..99820f48
--- /dev/null
+++ b/kamel-core/src/darwinMain/kotlin/io/kamel/core/mapper/Mappers.kt
@@ -0,0 +1,25 @@
+package io.kamel.core.mapper
+
+import io.kamel.core.utils.URI
+import io.kamel.core.utils.URL
+import io.ktor.http.*
+import kotlin.reflect.KClass
+
+internal actual val URLMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = URL::class
+ override val outputKClass: KClass
+ get() = Url::class
+
+ override fun map(input: URL): Url = StringMapper.map(input.absoluteString()!!)
+}
+
+
+internal actual val URIMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = URI::class
+ override val outputKClass: KClass
+ get() = Url::class
+
+ override fun map(input: URI): Url = StringMapper.map(input.uri)
+}
\ No newline at end of file
diff --git a/kamel-core/src/darwinMain/kotlin/io/kamel/core/utils/Platform.kt b/kamel-core/src/darwinMain/kotlin/io/kamel/core/utils/Platform.kt
new file mode 100644
index 00000000..a5565229
--- /dev/null
+++ b/kamel-core/src/darwinMain/kotlin/io/kamel/core/utils/Platform.kt
@@ -0,0 +1,44 @@
+package io.kamel.core.utils
+
+import kotlinx.cinterop.addressOf
+import kotlinx.cinterop.usePinned
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import platform.Foundation.NSData
+import platform.Foundation.NSFileHandle
+import platform.Foundation.NSURL
+import platform.Foundation.fileHandleForReadingAtPath
+import platform.posix.memcpy
+
+
+internal actual val Dispatchers.Kamel: CoroutineDispatcher get() = Default
+
+public actual class File(public val path: String) {
+
+ private val fileHandle: NSFileHandle? = NSFileHandle.fileHandleForReadingAtPath(path)
+ public val availableData: ByteArray get() = fileHandle?.availableData?.toByteArray() ?: byteArrayOf()
+ override fun toString(): String = path
+// memScoped {
+// fileHandle ?: return@memScoped "null"
+// println("File.toString()")
+// val buffer = CharArray(MAXPATHLEN) { 0.toChar() }
+// val result = buffer.usePinned { pinned ->
+// fcntl(fileHandle.fileDescriptor, F_GETPATH, pinned.addressOf(0))
+// }
+// println(result)
+// return@memScoped buffer.joinToString("")
+// }
+
+ private fun NSData.toByteArray(): ByteArray = ByteArray(this@toByteArray.length.toInt()).apply {
+ usePinned {
+ memcpy(it.addressOf(0), this@toByteArray.bytes, this@toByteArray.length)
+ }
+ }
+
+}
+
+public actual class URL(public val nsUrl: NSURL) {
+ public fun absoluteString(): String? = nsUrl.absoluteString
+}
+
+public actual class URI(public val uri: String)
\ No newline at end of file
diff --git a/kamel-core/src/darwinTest/kotlin/io/kamel/core/utils/MappersUtils.kt b/kamel-core/src/darwinTest/kotlin/io/kamel/core/utils/MappersUtils.kt
new file mode 100644
index 00000000..584f9c59
--- /dev/null
+++ b/kamel-core/src/darwinTest/kotlin/io/kamel/core/utils/MappersUtils.kt
@@ -0,0 +1,8 @@
+package io.kamel.core.utils
+
+import platform.Foundation.NSURL
+
+
+internal actual fun createURL(url: String): URL = URL(NSURL.URLWithString(url)!!)
+
+internal actual fun createURI(url: String): URI = URI(url)
\ No newline at end of file
diff --git a/kamel-core/src/jsMain/kotlin/io/kamel/core/fetcher/FileFetcher.kt b/kamel-core/src/jsMain/kotlin/io/kamel/core/fetcher/FileFetcher.kt
new file mode 100644
index 00000000..c763f3b8
--- /dev/null
+++ b/kamel-core/src/jsMain/kotlin/io/kamel/core/fetcher/FileFetcher.kt
@@ -0,0 +1,54 @@
+package io.kamel.core.fetcher
+
+import io.kamel.core.DataSource
+import io.kamel.core.Resource
+import io.kamel.core.config.ResourceConfig
+import io.kamel.core.utils.File
+import io.ktor.utils.io.*
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.suspendCancellableCoroutine
+import org.khronos.webgl.ArrayBuffer
+import org.khronos.webgl.Int8Array
+import org.w3c.dom.ErrorEvent
+import org.w3c.files.FileReader
+import kotlin.coroutines.resumeWithException
+import kotlin.reflect.KClass
+
+/**
+ * Fetcher that fetchers [ByteReadChannel] from a file.
+ */
+internal actual object FileFetcher : Fetcher {
+
+ override val inputDataKClass: KClass = File::class
+
+ override val source: DataSource = DataSource.Disk
+
+ override val File.isSupported: Boolean
+ get() = true
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override fun fetch(
+ data: File,
+ resourceConfig: ResourceConfig
+ ): Flow> = flow {
+ val byteReadChannel = ByteReadChannel(getBase64(data.file))
+ emit(Resource.Success(byteReadChannel, source))
+ }
+
+ @ExperimentalCoroutinesApi
+ private suspend fun getBase64(file: org.w3c.files.File): ByteArray = suspendCancellableCoroutine { continuation ->
+ val reader = FileReader()
+ reader.readAsArrayBuffer(file)
+ reader.onload = {
+ val arrayBuffer = reader.result as ArrayBuffer
+ val bytes = Int8Array(arrayBuffer).unsafeCast()
+ continuation.resume(bytes, null)
+ }
+ reader.onerror = { error ->
+ continuation.resumeWithException(Error((error as ErrorEvent).message))
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/kamel-core/src/jsMain/kotlin/io/kamel/core/mapper/Mappers.kt b/kamel-core/src/jsMain/kotlin/io/kamel/core/mapper/Mappers.kt
new file mode 100644
index 00000000..00bba534
--- /dev/null
+++ b/kamel-core/src/jsMain/kotlin/io/kamel/core/mapper/Mappers.kt
@@ -0,0 +1,25 @@
+package io.kamel.core.mapper
+
+import io.kamel.core.utils.URI
+import io.kamel.core.utils.URL
+import io.ktor.http.*
+import kotlin.reflect.KClass
+
+internal actual val URLMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = URL::class
+ override val outputKClass: KClass
+ get() = Url::class
+
+ override fun map(input: URL): Url = StringMapper.map(input.toString().removeSuffix("/"))
+}
+
+
+internal actual val URIMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = URI::class
+ override val outputKClass: KClass
+ get() = Url::class
+
+ override fun map(input: URI): Url = StringMapper.map(input.uri)
+}
\ No newline at end of file
diff --git a/kamel-core/src/jsMain/kotlin/io/kamel/core/utils/Platform.kt b/kamel-core/src/jsMain/kotlin/io/kamel/core/utils/Platform.kt
new file mode 100644
index 00000000..7f094885
--- /dev/null
+++ b/kamel-core/src/jsMain/kotlin/io/kamel/core/utils/Platform.kt
@@ -0,0 +1,18 @@
+package io.kamel.core.utils
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+
+
+internal actual val Dispatchers.Kamel: CoroutineDispatcher get() = Default
+
+public actual class File(public val file: org.w3c.files.File) {
+ override fun toString(): String {
+ return file.name
+ }
+}
+
+
+public actual typealias URL = org.w3c.dom.url.URL
+
+public actual class URI(public val uri: String)
\ No newline at end of file
diff --git a/kamel-core/src/jsTest/kotlin/io/kamel/core/utils/MappersUtils.kt b/kamel-core/src/jsTest/kotlin/io/kamel/core/utils/MappersUtils.kt
new file mode 100644
index 00000000..8f70f5e2
--- /dev/null
+++ b/kamel-core/src/jsTest/kotlin/io/kamel/core/utils/MappersUtils.kt
@@ -0,0 +1,7 @@
+package io.kamel.core.utils
+
+
+
+internal actual fun createURL(url: String): URL = URL(url)
+
+internal actual fun createURI(url: String): URI = URI(url)
\ No newline at end of file
diff --git a/kamel-core/src/jvmMain/kotlin/io/kamel/core/fetcher/JvmFileFetcher.kt b/kamel-core/src/jvmMain/kotlin/io/kamel/core/fetcher/JvmFileFetcher.kt
index dd2d7fec..ca988386 100644
--- a/kamel-core/src/jvmMain/kotlin/io/kamel/core/fetcher/JvmFileFetcher.kt
+++ b/kamel-core/src/jvmMain/kotlin/io/kamel/core/fetcher/JvmFileFetcher.kt
@@ -8,12 +8,15 @@ import io.ktor.utils.io.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.io.File
+import kotlin.reflect.KClass
/**
* Fetcher that fetchers [ByteReadChannel] from a file.
*/
internal actual object FileFetcher : Fetcher {
+ override val inputDataKClass: KClass = File::class
+
override val source: DataSource = DataSource.Disk
override val File.isSupported: Boolean
diff --git a/kamel-core/src/jvmMain/kotlin/io/kamel/core/mapper/JvmMappers.kt b/kamel-core/src/jvmMain/kotlin/io/kamel/core/mapper/JvmMappers.kt
index 78e8e3f9..5fd23a9c 100644
--- a/kamel-core/src/jvmMain/kotlin/io/kamel/core/mapper/JvmMappers.kt
+++ b/kamel-core/src/jvmMain/kotlin/io/kamel/core/mapper/JvmMappers.kt
@@ -3,7 +3,22 @@ package io.kamel.core.mapper
import io.kamel.core.utils.URI
import io.kamel.core.utils.URL
import io.ktor.http.*
+import kotlin.reflect.KClass
-internal actual val URLMapper: Mapper = Mapper { URIMapper.map(it.toURI()) }
+internal actual val URLMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = URL::class
+ override val outputKClass: KClass
+ get() = Url::class
-internal actual val URIMapper: Mapper = Mapper { Url(it) }
\ No newline at end of file
+ override fun map(input: URL): Url = Url(input.toURI())
+}
+
+internal actual val URIMapper: Mapper = object : Mapper {
+ override val inputKClass: KClass
+ get() = URI::class
+ override val outputKClass: KClass
+ get() = Url::class
+
+ override fun map(input: URI): Url = Url(input)
+}
\ No newline at end of file
diff --git a/kamel-core/src/jvmMain/kotlin/io/kamel/core/utils/JvmPlatform.kt b/kamel-core/src/jvmMain/kotlin/io/kamel/core/utils/JvmPlatform.kt
index 4df03255..29cde4b9 100644
--- a/kamel-core/src/jvmMain/kotlin/io/kamel/core/utils/JvmPlatform.kt
+++ b/kamel-core/src/jvmMain/kotlin/io/kamel/core/utils/JvmPlatform.kt
@@ -2,14 +2,13 @@ package io.kamel.core.utils
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
-import java.io.File
import java.net.URI
import java.net.URL
-internal actual val Dispatchers.IO: CoroutineDispatcher get() = IO
+internal actual val Dispatchers.Kamel: CoroutineDispatcher get() = IO
-internal actual typealias File = File
+public actual typealias File = java.io.File
-internal actual typealias URL = URL
+public actual typealias URL = URL
-internal actual typealias URI = URI
\ No newline at end of file
+public actual typealias URI = URI
\ No newline at end of file
diff --git a/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/cache/LruCache.kt b/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/cache/LruCache.kt
new file mode 100644
index 00000000..7cd3a565
--- /dev/null
+++ b/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/cache/LruCache.kt
@@ -0,0 +1,26 @@
+package io.kamel.core.cache
+
+
+/**
+ * Cache implementation which evicts items using an LRU algorithm.
+ */
+internal actual class LruCache actual constructor(override val maxSize: Int) : Cache {
+
+ private val cache: io.kamel.core.cache.common.LruCache = io.kamel.core.cache.common.LruCache(maxSize)
+
+ override val size: Int
+ get() = cache.size()
+
+ init {
+ require(maxSize >= 0) { "Cache max size must be positive number" }
+ }
+
+ override fun get(key: K): V? = cache[key]
+
+ override fun set(key: K, value: V) = cache.set(key, value)
+
+ override fun remove(key: K): Boolean = cache.remove(key) != null
+
+ override fun clear(): Unit = cache.clear()
+
+}
\ No newline at end of file
diff --git a/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/cache/common/LruCache.kt b/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/cache/common/LruCache.kt
new file mode 100644
index 00000000..aa0f16fa
--- /dev/null
+++ b/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/cache/common/LruCache.kt
@@ -0,0 +1,161 @@
+package io.kamel.core.cache.common
+
+internal typealias Weigher = (Key, Value?) -> Int
+
+/**
+ * Multiplatform LRU cache implementation.
+ * https://github.com/apollographql/apollo-kotlin/blob/main/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/LruCache.kt
+ *
+ * Implementation is based on usage of [LinkedHashMap] as a container for the cache and custom
+ * double linked queue to track LRU property.
+ *
+ * [maxSize] - maximum size of the cache, can be anything bytes, number of entries etc. By default is number o entries.
+ * [weigher] - to be called to calculate the estimated size (weight) of the cache entry defined by its [Key] and [Value].
+ * By default it returns 1.
+ *
+ * Cache trim performed only on new entry insertion.
+ */
+internal class LruCache(
+ private val maxSize: Int,
+ private val weigher: Weigher = { _, _ -> 1 },
+) {
+ private val cache = LinkedHashMap>(0, 0.75f)
+ private var headNode: Node? = null
+ private var tailNode: Node? = null
+ private var size: Int = 0
+
+ operator fun get(key: Key): Value? {
+ val node = cache[key]
+ if (node != null) {
+ moveNodeToHead(node)
+ }
+ return node?.value
+ }
+
+ operator fun set(key: Key, value: Value) {
+ val node = cache[key]
+ if (node == null) {
+ cache[key] = addNode(key, value)
+ } else {
+ node.value = value
+ moveNodeToHead(node)
+ }
+
+ trim()
+ }
+
+ fun remove(key: Key): Value? {
+ return removeUnsafe(key)
+ }
+
+ fun keys() = cache.keys
+
+ private fun removeUnsafe(key: Key): Value? {
+ val nodeToRemove = cache.remove(key)
+ val value = nodeToRemove?.value
+ if (nodeToRemove != null) {
+ unlinkNode(nodeToRemove)
+ }
+ return value
+ }
+
+ fun remove(keys: Collection) {
+ keys.forEach { key -> removeUnsafe(key) }
+ }
+
+ fun clear() {
+ cache.clear()
+ headNode = null
+ tailNode = null
+ size = 0
+ }
+
+ fun size(): Int {
+ return size
+ }
+
+ fun dump(): Map {
+ return cache.mapValues { (_, value) ->
+ @Suppress("UNCHECKED_CAST")
+ value.value as Value
+ }
+ }
+
+ private fun trim() {
+ var nodeToRemove = tailNode
+ while (nodeToRemove != null && size > maxSize) {
+ cache.remove(nodeToRemove.key)
+ unlinkNode(nodeToRemove)
+ nodeToRemove = tailNode
+ }
+ }
+
+ private fun addNode(key: Key, value: Value?): Node {
+ val node = Node(
+ key = key,
+ value = value,
+ next = headNode,
+ prev = null,
+ )
+
+ headNode = node
+
+ if (node.next == null) {
+ tailNode = headNode
+ } else {
+ node.next?.prev = headNode
+ }
+
+ size += weigher(key, value)
+
+ return node
+ }
+
+ private fun moveNodeToHead(node: Node) {
+ if (node.prev == null) {
+ return
+ }
+
+ node.prev?.next = node.next
+
+ if (node.next == null) {
+ tailNode = node.prev
+ } else {
+ node.next?.prev = node.prev
+ }
+
+ node.next = headNode
+ node.prev = null
+
+ headNode?.prev = node
+ headNode = node
+ }
+
+ private fun unlinkNode(node: Node) {
+ if (node.prev == null) {
+ this.headNode = node.next
+ } else {
+ node.prev?.next = node.next
+ }
+
+ if (node.next == null) {
+ this.tailNode = node.prev
+ } else {
+ node.next?.prev = node.prev
+ }
+
+ size -= weigher(node.key!!, node.value)
+
+ node.key = null
+ node.value = null
+ node.next = null
+ node.prev = null
+ }
+
+ private class Node(
+ var key: Key?,
+ var value: Value?,
+ var next: Node?,
+ var prev: Node?,
+ )
+}
\ No newline at end of file
diff --git a/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/utils/ConfigUtils.kt b/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/utils/ConfigUtils.kt
new file mode 100644
index 00000000..7ae031e8
--- /dev/null
+++ b/kamel-core/src/nonJvmMain/kotlin/io/kamel/core/utils/ConfigUtils.kt
@@ -0,0 +1,41 @@
+@file:Suppress("UNCHECKED_CAST")
+
+package io.kamel.core.utils
+
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.decoder.Decoder
+import io.kamel.core.fetcher.Fetcher
+
+internal actual fun KamelConfig.findFetcherFor(data: T): Fetcher {
+
+ val type = data::class
+
+ val fetcher = fetchers.findLast { fetcher ->
+
+ val fetcherType = fetcher.inputDataKClass
+
+ val isSameType = fetcherType == type
+
+ isSameType && with(fetcher) { data.isSupported }
+ }
+
+ checkNotNull(fetcher) { "Unable to find a fetcher for $type" }
+
+ return fetcher as Fetcher
+}
+
+internal actual inline fun KamelConfig.findDecoderFor(): Decoder {
+
+ val type = T::class
+
+ val decoder = decoders.findLast { decoder ->
+
+ val decoderType = decoder.outputKClass
+
+ decoderType == type
+ }
+
+ checkNotNull(decoder) { "Unable to find a decoder for $type" }
+
+ return decoder as Decoder
+}
diff --git a/kamel-image/build.gradle.kts b/kamel-image/build.gradle.kts
index 7a616cfb..5b658e6a 100644
--- a/kamel-image/build.gradle.kts
+++ b/kamel-image/build.gradle.kts
@@ -1,4 +1,3 @@
-import org.jetbrains.compose.compose
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
plugins {
@@ -14,13 +13,12 @@ android {
defaultConfig {
minSdk = 21
- targetSdk = 33
multiDexEnabled = true
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
testOptions {
@@ -29,22 +27,6 @@ android {
}
}
- sourceSets {
- named("main") {
- manifest.srcFile("src/androidMain/AndroidManifest.xml")
- res.srcDirs("src/androidMain/res", "src/commonMain/resources")
- }
- }
-
- configurations {
- create("androidTestApi")
- create("androidTestDebugApi")
- create("androidTestReleaseApi")
- create("testApi")
- create("testDebugApi")
- create("testReleaseApi")
- }
-
}
kotlin {
@@ -55,59 +37,115 @@ kotlin {
publishAllLibraryVariants()
}
jvm("desktop")
+ js(IR) {
+ browser()
+ }
+ for (target in Targets.nativeTargets) {
+ targets.add(presets.getByName(target).createTarget(target))
+ }
sourceSets {
val commonMain by getting {
dependencies {
api(project(":kamel-core"))
+ implementation(compose.ui)
+ implementation(compose.foundation)
+ implementation(compose.runtime)
}
}
val commonTest by getting {
dependencies {
implementation(project(":kamel-tests"))
- implementation(kotlin("test-common"))
- implementation(kotlin("test-annotations-common"))
+ implementation(kotlin("test"))
implementation(Dependencies.Testing.Ktor)
+ implementation(Dependencies.Coroutines.Test)
+ }
+ }
+
+ val jvmMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val jvmTest by creating {
+ dependsOn(commonTest)
+ dependencies {
+ implementation(compose.material)
implementation(Dependencies.Testing.Compose)
}
}
- val desktopMain by getting
+ val desktopMain by getting {
+ dependsOn(jvmMain)
+ }
val desktopTest by getting {
+ dependsOn(jvmTest)
dependencies {
implementation(Dependencies.Ktor.CIO)
implementation(compose.desktop.currentOs)
- implementation(kotlin("test-junit"))
}
}
- val androidMain by getting
+ val androidMain by getting {
+ dependsOn(jvmMain)
+ }
- val androidTest by getting {
+ val androidUnitTest by getting {
+ dependsOn(jvmTest)
+ }
+
+ val nonJvmMain by creating {
+ dependsOn(commonMain)
dependencies {
- implementation(kotlin("test"))
- implementation(kotlin("test-junit"))
- implementation("androidx.test:core:1.4.0")
- implementation("androidx.test.ext:junit:1.1.3")
+ implementation(Dependencies.XmlUtil.Serialization)
}
}
- all {
- languageSettings.apply {
- optIn("kotlin.Experimental")
+ val nonJvmTest by creating {
+ dependsOn(commonTest)
+ }
+
+ val jsMain by getting {
+ dependsOn(nonJvmMain)
+ dependencies {
+ implementation(Dependencies.Ktor.Js)
+ }
+ }
+
+ val darwinMain by creating {
+ dependsOn(nonJvmMain)
+ dependencies {
+ implementation(Dependencies.Ktor.Darwin)
}
}
- targets.all {
- compilations.all {
- kotlinOptions {
- freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn")
- }
+ val darwinTest by creating {
+ dependsOn(nonJvmTest)
+ }
+
+ Targets.darwinTargets.forEach { target ->
+ getByName("${target}Main") {
+ dependsOn(darwinMain)
+ }
+ getByName("${target}Test") {
+ dependsOn(darwinTest)
+ }
+ }
+
+ all {
+ languageSettings.apply {
+ optIn("kotlin.Experimental")
}
}
}
}
+
+// https://youtrack.jetbrains.com/issue/KT-46466
+val dependsOnTasks = mutableListOf()
+tasks.withType().configureEach {
+ dependsOnTasks.add(this.name.replace("publish", "sign").replaceAfter("Publication", ""))
+ dependsOn(dependsOnTasks)
+}
\ No newline at end of file
diff --git a/kamel-image/src/androidMain/kotlin/io/kamel/image/decoder/AndroidImageBitmapDecoder.kt b/kamel-image/src/androidMain/kotlin/io/kamel/image/decoder/AndroidImageBitmapDecoder.kt
index f395209c..3edda718 100644
--- a/kamel-image/src/androidMain/kotlin/io/kamel/image/decoder/AndroidImageBitmapDecoder.kt
+++ b/kamel-image/src/androidMain/kotlin/io/kamel/image/decoder/AndroidImageBitmapDecoder.kt
@@ -8,11 +8,14 @@ import io.kamel.core.config.ResourceConfig
import io.kamel.core.decoder.Decoder
import io.ktor.util.*
import io.ktor.utils.io.*
+import kotlin.reflect.KClass
private const val Offset = 0
internal actual object ImageBitmapDecoder : Decoder {
+ override val outputKClass: KClass = ImageBitmap::class
+
override suspend fun decode(channel: ByteReadChannel, resourceConfig: ResourceConfig): ImageBitmap {
val bytes = channel.toByteArray()
val bitmap = BitmapFactory.decodeByteArray(bytes, Offset, bytes.size) as Bitmap
diff --git a/kamel-image/src/androidMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt b/kamel-image/src/androidMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt
index 212a1835..849aa2d1 100644
--- a/kamel-image/src/androidMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt
+++ b/kamel-image/src/androidMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt
@@ -12,10 +12,13 @@ import io.ktor.utils.io.*
import io.ktor.utils.io.jvm.javaio.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
+import kotlin.reflect.KClass
internal class ResourcesFetcher(private val context: Context) : Fetcher {
+ override val inputDataKClass: KClass = Url::class
+
override val source: DataSource = DataSource.Disk
override val Url.isSupported: Boolean
diff --git a/kamel-image/src/androidMain/kotlin/io/kamel/image/mapper/ResourcesIdMapper.kt b/kamel-image/src/androidMain/kotlin/io/kamel/image/mapper/ResourcesIdMapper.kt
index caf2650f..1973c1f6 100644
--- a/kamel-image/src/androidMain/kotlin/io/kamel/image/mapper/ResourcesIdMapper.kt
+++ b/kamel-image/src/androidMain/kotlin/io/kamel/image/mapper/ResourcesIdMapper.kt
@@ -5,10 +5,17 @@ import android.content.Context
import androidx.annotation.DrawableRes
import io.kamel.core.mapper.Mapper
import io.ktor.http.*
+import kotlin.reflect.KClass
internal class ResourcesIdMapper(private val context: Context) : Mapper<@receiver:DrawableRes Int, Url> {
+ override val inputKClass: KClass
+ get() = Int::class
+
+ override val outputKClass: KClass
+ get() = Url::class
+
override fun map(@DrawableRes input: Int): Url {
val packageName = context.packageName
val protocol = URLProtocol(name = ContentResolver.SCHEME_ANDROID_RESOURCE, defaultPort = -1)
diff --git a/kamel-image/src/commonMain/kotlin/io/kamel/image/LazyPainterResource.kt b/kamel-image/src/commonMain/kotlin/io/kamel/image/LazyPainterResource.kt
index b86326c4..49f9d57a 100644
--- a/kamel-image/src/commonMain/kotlin/io/kamel/image/LazyPainterResource.kt
+++ b/kamel-image/src/commonMain/kotlin/io/kamel/image/LazyPainterResource.kt
@@ -15,6 +15,8 @@ import io.kamel.core.config.ResourceConfigBuilder
import io.kamel.image.config.LocalKamelConfig
import io.ktor.http.*
+public class PainterFailure : Error("Failed to return a Painter")
+
/**
* Loads a [Painter] resource asynchronously.
* @param data Can be anything such as [String], [Url] or a [File].
@@ -32,13 +34,13 @@ import io.ktor.http.*
*/
@ExperimentalKamelApi
@Composable
-public inline fun lazyPainterResource(
- data: Any,
+public inline fun lazyPainterResource(
+ data: I,
key: Any? = data,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
- noinline onLoadingPainter: @Composable (Float) -> Painter? = { null },
- noinline onFailurePainter: @Composable (Throwable) -> Painter? = { null },
- block: ResourceConfigBuilder.() -> Unit = {},
+ noinline onLoadingPainter: @Composable (Float) -> Result = { Result.failure(PainterFailure()) },
+ noinline onFailurePainter: @Composable (Throwable) -> Result = { Result.failure(PainterFailure()) },
+ crossinline block: ResourceConfigBuilder.() -> Unit = {},
): Resource {
val kamelConfig = LocalKamelConfig.current
@@ -61,14 +63,17 @@ public inline fun lazyPainterResource(
val painterResourceWithFallbacks = when (painterResource) {
is Resource.Loading -> {
val resource = painterResource as Resource.Loading
- val painter = onLoadingPainter(resource.progress)
- if (painter != null) Resource.Success(painter) else painterResource
+ onLoadingPainter(resource.progress)
+ .mapCatching { painter -> Resource.Success(painter) }
+ .getOrDefault(painterResource)
}
+
is Resource.Success -> painterResource
is Resource.Failure -> {
val resource = painterResource as Resource.Failure
- val painter = onFailurePainter(resource.exception)
- if (painter != null) Resource.Success(painter) else painterResource
+ onFailurePainter(resource.exception)
+ .mapCatching { painter -> Resource.Success(painter) }
+ .getOrDefault(painterResource)
}
}
@@ -83,6 +88,7 @@ public inline fun lazyPainterResource(
}
}
+
/**
* Loads a [Painter] resource asynchronously.
* @param data Can be anything such as [String], [Url] or a [File].
@@ -98,12 +104,12 @@ public inline fun lazyPainterResource(
data: Any,
key: Any? = data,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
- block: ResourceConfigBuilder.() -> Unit = {},
+ crossinline block: ResourceConfigBuilder.() -> Unit = {},
): Resource = lazyPainterResource(
data,
key,
filterQuality,
- onLoadingPainter = { null },
- onFailurePainter = { null },
+ onLoadingPainter = { Result.failure(PainterFailure()) },
+ onFailurePainter = { Result.failure(PainterFailure()) },
block
)
\ No newline at end of file
diff --git a/kamel-image/src/darwinMain/kotlin/attributeOrNull.kt b/kamel-image/src/darwinMain/kotlin/attributeOrNull.kt
new file mode 100644
index 00000000..1085ca54
--- /dev/null
+++ b/kamel-image/src/darwinMain/kotlin/attributeOrNull.kt
@@ -0,0 +1,15 @@
+import nl.adaptivity.xmlutil.dom.Element
+
+/***
+ * Accessing by namespace does not appear to be working with https://github.com/pdvrieze/xmlutil v0.86.0
+ * note: on native ":" had to be prepended to name to load attributes 🤷
+ * todo: figure out how to handle namespaces
+ */
+internal actual fun Element.attributeOrNull(
+ @Suppress("UNUSED_PARAMETER") namespace: String,
+ name: String
+): String? {
+// val value = getAttributeNS(namespace,":$name")
+ val value = getAttribute(":$name")
+ return value?.ifBlank { null }
+}
\ No newline at end of file
diff --git a/kamel-image/src/darwinMain/kotlin/filterIsElement.kt b/kamel-image/src/darwinMain/kotlin/filterIsElement.kt
new file mode 100644
index 00000000..c0edaa22
--- /dev/null
+++ b/kamel-image/src/darwinMain/kotlin/filterIsElement.kt
@@ -0,0 +1,5 @@
+import nl.adaptivity.xmlutil.dom.Element
+import nl.adaptivity.xmlutil.dom.Node
+
+internal actual fun Sequence.filterIsElement(): Sequence =
+ filterIsInstance()
\ No newline at end of file
diff --git a/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageBitmapDecoder.kt b/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageBitmapDecoder.kt
index 702decee..5d2c6c6f 100644
--- a/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageBitmapDecoder.kt
+++ b/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageBitmapDecoder.kt
@@ -7,12 +7,15 @@ import io.kamel.core.decoder.Decoder
import io.ktor.util.*
import io.ktor.utils.io.*
import org.jetbrains.skia.Image
+import kotlin.reflect.KClass
/**
* Decodes and transfers [ByteReadChannel] to [ImageBitmap] using Skia [Image].
*/
internal actual object ImageBitmapDecoder : Decoder {
+ override val outputKClass: KClass = ImageBitmap::class
+
override suspend fun decode(
channel: ByteReadChannel,
resourceConfig: ResourceConfig
diff --git a/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageVectorDecoder.kt b/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageVectorDecoder.kt
index eba171d2..0cc0cae2 100644
--- a/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageVectorDecoder.kt
+++ b/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopImageVectorDecoder.kt
@@ -1,5 +1,6 @@
package io.kamel.image.decoder
+import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.loadXmlImageVector
import io.kamel.core.config.ResourceConfig
@@ -7,8 +8,12 @@ import io.kamel.core.decoder.Decoder
import io.ktor.utils.io.*
import io.ktor.utils.io.jvm.javaio.*
import org.xml.sax.InputSource
+import kotlin.reflect.KClass
internal object ImageVectorDecoder : Decoder {
+
+ override val outputKClass: KClass = ImageVector::class
+
override suspend fun decode(channel: ByteReadChannel, resourceConfig: ResourceConfig): ImageVector {
val inputSource = InputSource(channel.toInputStream())
return loadXmlImageVector(inputSource, resourceConfig.density)
diff --git a/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopSvgDecoder.kt b/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopSvgDecoder.kt
index bbb7f263..16ce2fae 100644
--- a/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopSvgDecoder.kt
+++ b/kamel-image/src/desktopMain/kotlin/io/kamel/image/decoder/DesktopSvgDecoder.kt
@@ -1,13 +1,18 @@
package io.kamel.image.decoder
import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.loadSvgPainter
import io.kamel.core.config.ResourceConfig
import io.kamel.core.decoder.Decoder
import io.ktor.utils.io.*
import io.ktor.utils.io.jvm.javaio.*
+import kotlin.reflect.KClass
internal object SvgDecoder : Decoder {
+
+ override val outputKClass: KClass = Painter::class
+
override suspend fun decode(channel: ByteReadChannel, resourceConfig: ResourceConfig): Painter {
return loadSvgPainter(
channel.toInputStream(),
diff --git a/kamel-image/src/desktopMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt b/kamel-image/src/desktopMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt
index a76794eb..4c0e2d76 100644
--- a/kamel-image/src/desktopMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt
+++ b/kamel-image/src/desktopMain/kotlin/io/kamel/image/fetcher/ResourcesFetcher.kt
@@ -9,9 +9,12 @@ import io.ktor.http.*
import io.ktor.utils.io.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
+import kotlin.reflect.KClass
internal object ResourcesFetcher : Fetcher {
+ override val inputDataKClass: KClass = Url::class
+
override val source: DataSource = DataSource.Disk
override val Url.isSupported: Boolean
diff --git a/kamel-image/src/desktopTest/kotlin/io/kamel/image/fetcher/ResourcesFetcherTest.kt b/kamel-image/src/desktopTest/kotlin/io/kamel/image/fetcher/ResourcesFetcherTest.kt
index 98e8acc1..637a57b7 100644
--- a/kamel-image/src/desktopTest/kotlin/io/kamel/image/fetcher/ResourcesFetcherTest.kt
+++ b/kamel-image/src/desktopTest/kotlin/io/kamel/image/fetcher/ResourcesFetcherTest.kt
@@ -22,15 +22,15 @@ class ResourcesFetcherTest {
private val resourceConfig = ResourceConfigBuilder().build()
@Test
- fun testUrlIsSupported(): Unit = runTest {
- val imageUrl = Url("Compose.png")
+ fun testUrlIsSupported() = runTest {
+ val imageUrl = Url("files/Compose.png")
val isSupported = with(fetcher) { imageUrl.isSupported }
assertTrue { isSupported }
}
@Test
- fun testUrlIsNotSupported(): Unit = runTest {
+ fun testUrlIsNotSupported() = runTest {
val imageUrl = Url("invalidImage.jpg")
val isSupported = with(fetcher) { imageUrl.isSupported }
@@ -38,8 +38,8 @@ class ResourcesFetcherTest {
}
@Test
- fun loadImageBitmapResource(): Unit = runTest {
- val imageUrl = Url("Compose.png")
+ fun loadImageBitmapResource() = runTest {
+ val imageUrl = Url("files/Compose.png")
val resource = fetcher.fetch(imageUrl, resourceConfig)
.first { !it.isLoading }
.map { it.toByteArray() }
@@ -49,7 +49,7 @@ class ResourcesFetcherTest {
}
@Test
- fun loadInvalidImageResource(): Unit = runTest {
+ fun loadInvalidImageResource() = runTest {
val imageUrl = Url("invalidImage.jpg")
assertFailsWith {
diff --git a/kamel-image/src/jsMain/kotlin/attributeOrNull.kt b/kamel-image/src/jsMain/kotlin/attributeOrNull.kt
new file mode 100644
index 00000000..927c508e
--- /dev/null
+++ b/kamel-image/src/jsMain/kotlin/attributeOrNull.kt
@@ -0,0 +1,15 @@
+import nl.adaptivity.xmlutil.dom.Element
+
+/***
+ * Accessing by namespace does not appear to be working with https://github.com/pdvrieze/xmlutil v0.86.0
+ * note: on native ":" had to be prepended to name to load attributes 🤷
+ * todo: figure out how to handle namespaces
+ */
+internal actual fun Element.attributeOrNull(
+ @Suppress("UNUSED_PARAMETER") namespace: String,
+ name: String
+): String? {
+// val value = getAttributeNS(namespace, name)
+ val value = getAttribute(name)
+ return value?.ifBlank { null }
+}
\ No newline at end of file
diff --git a/kamel-image/src/jsMain/kotlin/filterIsElement.kt b/kamel-image/src/jsMain/kotlin/filterIsElement.kt
new file mode 100644
index 00000000..ff73947a
--- /dev/null
+++ b/kamel-image/src/jsMain/kotlin/filterIsElement.kt
@@ -0,0 +1,9 @@
+import nl.adaptivity.js.util.asElement
+import nl.adaptivity.xmlutil.dom.Element
+import nl.adaptivity.xmlutil.dom.Node
+
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
+internal actual fun Sequence.filterIsElement(): Sequence =
+ mapNotNull {
+ (it as org.w3c.dom.Node).asElement() as Element?
+ }
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/DrawCache.kt b/kamel-image/src/nonJvmMain/kotlin/DrawCache.kt
new file mode 100644
index 00000000..58c83b0e
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/DrawCache.kt
@@ -0,0 +1,91 @@
+import androidx.compose.ui.graphics.BlendMode
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.toSize
+
+/**
+ * Creates a drawing environment that directs its drawing commands to an [ImageBitmap]
+ * which can be drawn directly in another [DrawScope] instance. This is useful to cache
+ * complicated drawing commands across frames especially if the content has not changed.
+ * Additionally some drawing operations such as rendering paths are done purely in
+ * software so it is beneficial to cache the result and render the contents
+ * directly through a texture as done by [DrawScope.drawImage]
+ */
+// Note copied from here:
+// https://github.com/JetBrains/compose-multiplatform-core/blob/5c26b7b9f5619ee4f319c6caf43192851b8ee15e/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt#L39
+// todo: remove when available in common androidx
+internal class DrawCache {
+
+ @PublishedApi internal var mCachedImage: ImageBitmap? = null
+ private var cachedCanvas: Canvas? = null
+ private var scopeDensity: Density? = null
+ private var layoutDirection: LayoutDirection = LayoutDirection.Ltr
+ private var size: IntSize = IntSize.Zero
+
+ private val cacheScope = CanvasDrawScope()
+
+ /**
+ * Draw the contents of the lambda with receiver scope into an [ImageBitmap] with the provided
+ * size. If the same size is provided across calls, the same [ImageBitmap] instance is
+ * re-used and the contents are cleared out before drawing content in it again
+ */
+ fun drawCachedImage(
+ size: IntSize,
+ density: Density,
+ layoutDirection: LayoutDirection,
+ block: DrawScope.() -> Unit
+ ) {
+ this.scopeDensity = density
+ this.layoutDirection = layoutDirection
+ var targetImage = mCachedImage
+ var targetCanvas = cachedCanvas
+ if (targetImage == null ||
+ targetCanvas == null ||
+ size.width > targetImage.width ||
+ size.height > targetImage.height
+ ) {
+ targetImage = ImageBitmap(size.width, size.height)
+ targetCanvas = Canvas(targetImage)
+
+ mCachedImage = targetImage
+ cachedCanvas = targetCanvas
+ }
+ this.size = size
+ cacheScope.draw(density, layoutDirection, targetCanvas, size.toSize()) {
+ clear()
+ block()
+ }
+ targetImage.prepareToDraw()
+ }
+
+ /**
+ * Draw the cached content into the provided [DrawScope] instance
+ */
+ fun drawInto(
+ target: DrawScope,
+ alpha: Float = 1.0f,
+ colorFilter: ColorFilter? = null
+ ) {
+ val targetImage = mCachedImage
+ check(targetImage != null) {
+ "drawCachedImage must be invoked first before attempting to draw the result " +
+ "into another destination"
+ }
+ target.drawImage(targetImage, srcSize = size, alpha = alpha, colorFilter = colorFilter)
+ }
+
+ /**
+ * Helper method to clear contents of the draw environment from the given bounds of the
+ * DrawScope
+ */
+ private fun DrawScope.clear() {
+ drawRect(color = Color.Black, blendMode = BlendMode.Clear)
+ }
+}
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/ValueParsers.kt b/kamel-image/src/nonJvmMain/kotlin/ValueParsers.kt
new file mode 100644
index 00000000..4f259529
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/ValueParsers.kt
@@ -0,0 +1,105 @@
+/*
+ * 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 androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.TileMode
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+// copied from
+// https://github.com/JetBrains/compose-multiplatform-core/blob/5c26b7b9f5619ee4f319c6caf43192851b8ee15e/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/vector/DesktopValueParsers.desktop.kt#L29
+
+private const val ALPHA_MASK = 0xFF000000.toInt()
+
+// parseColorValue is copied from Android:
+// https://cs.android.com/android-studio/platform/tools/base/+/05fadd8cb2aaafb77da02048c7a240b2147ff293:sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdUtil.kt;l=58
+/**
+ * Parses a color value in #AARRGGBB format.
+ *
+ * @param color the color value string
+ * @return the integer color value
+ */
+internal fun parseColorValue(color: String): Int {
+ require(color.startsWith("#")) { "Invalid color value $color" }
+
+ return when (color.length) {
+ 7 -> {
+ // #RRGGBB
+ color.substring(1).toUInt( 16).toInt() or ALPHA_MASK
+ }
+ 9 -> {
+ // #AARRGGBB
+ color.substring(1).toUInt( 16).toInt()
+ }
+ 4 -> {
+ // #RGB
+ val v = color.substring(1).toUInt( 16).toInt()
+ var k = (v shr 8 and 0xF) * 0x110000
+ k = k or (v shr 4 and 0xF) * 0x1100
+ k = k or (v and 0xF) * 0x11
+ k or ALPHA_MASK
+ }
+ 5 -> {
+ // #ARGB
+ val v = color.substring(1).toUInt( 16).toInt()
+ var k = (v shr 12 and 0xF) * 0x11000000
+ k = k or (v shr 8 and 0xF) * 0x110000
+ k = k or (v shr 4 and 0xF) * 0x1100
+ k = k or (v and 0xF) * 0x11
+ k or ALPHA_MASK
+ }
+ else -> ALPHA_MASK
+ }
+}
+
+internal fun parseFillType(fillType: String): PathFillType = when (fillType) {
+ "nonZero" -> PathFillType.NonZero
+ "evenOdd" -> PathFillType.EvenOdd
+ else -> throw UnsupportedOperationException("unknown fillType: $fillType")
+}
+
+internal fun parseStrokeCap(strokeCap: String): StrokeCap = when (strokeCap) {
+ "butt" -> StrokeCap.Butt
+ "round" -> StrokeCap.Round
+ "square" -> StrokeCap.Square
+ else -> throw UnsupportedOperationException("unknown strokeCap: $strokeCap")
+}
+
+internal fun parseStrokeJoin(strokeJoin: String): StrokeJoin = when (strokeJoin) {
+ "miter" -> StrokeJoin.Miter
+ "round" -> StrokeJoin.Round
+ "bevel" -> StrokeJoin.Bevel
+ else -> throw UnsupportedOperationException("unknown strokeJoin: $strokeJoin")
+}
+
+internal fun parseTileMode(tileMode: String): TileMode = when (tileMode) {
+ "clamp" -> TileMode.Clamp
+ "repeated" -> TileMode.Repeated
+ "mirror" -> TileMode.Mirror
+ else -> throw throw UnsupportedOperationException("unknown tileMode: $tileMode")
+}
+
+internal fun String?.parseDp(density: Density): Dp = with(density) {
+ return when {
+ this@parseDp == null -> 0f.dp
+ endsWith("dp") -> removeSuffix("dp").toFloat().dp
+ endsWith("px") -> removeSuffix("px").toFloat().toDp()
+ else -> throw UnsupportedOperationException("value should ends with dp or px")
+ }
+}
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/XmlVectorParser.kt b/kamel-image/src/nonJvmMain/kotlin/XmlVectorParser.kt
new file mode 100644
index 00000000..8a526b4b
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/XmlVectorParser.kt
@@ -0,0 +1,264 @@
+/*
+ * 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 androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.*
+import androidx.compose.ui.graphics.vector.*
+import androidx.compose.ui.unit.Density
+import nl.adaptivity.xmlutil.dom.*
+
+// Parsing logic is the same as in Android implementation
+// (compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlVectorParser.kt)
+//
+// Except there is no support for linking with external resources
+// (for example, we can't reference to color defined in another file)
+//
+// Specification:
+// https://developer.android.com/reference/android/graphics/drawable/VectorDrawable
+
+// ported from
+// https://github.com/JetBrains/compose-multiplatform-core/blob/5c26b7b9f5619ee4f319c6caf43192851b8ee15e/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/vector/DesktopXmlVectorParser.desktop.kt#L72
+// to use https://github.com/pdvrieze/xmlutil
+
+private const val ANDROID_NS = "http://schemas.android.com/apk/res/android"
+private const val AAPT_NS = "http://schemas.android.com/aapt"
+
+private class BuildContext {
+ val currentGroups = mutableListOf()
+
+ enum class Group {
+ /**
+ * Group that exists in xml file
+ */
+ Real,
+
+ /**
+ * Group that doesn't exist in xml file. We add it manually when we see node.
+ * It will be automatically popped when the real group will be popped.
+ */
+ Virtual
+ }
+}
+
+internal fun Element.parseVectorRoot(density: Density): ImageVector {
+ val context = BuildContext()
+ val builder = ImageVector.Builder(
+ defaultWidth = attributeOrNull(ANDROID_NS, "width").parseDp(density),
+ defaultHeight = attributeOrNull(ANDROID_NS, "height").parseDp(density),
+ viewportWidth = attributeOrNull(ANDROID_NS, "viewportWidth")?.toFloat() ?: 0f,
+ viewportHeight = attributeOrNull(ANDROID_NS, "viewportHeight")?.toFloat() ?: 0f
+ )
+ parseVectorNodes(builder, context)
+ return builder.build()
+}
+
+private fun Element.parseVectorNodes(builder: ImageVector.Builder, context: BuildContext) {
+ childrenSequence
+ .filterIsElement()
+ .forEach {
+ it.parseVectorNode(builder, context)
+ }
+}
+
+private fun Element.parseVectorNode(builder: ImageVector.Builder, context: BuildContext) {
+ when (nodeName) {
+ "path" -> parsePath(builder)
+ "clip-path" -> parseClipPath(builder, context)
+ "group" -> parseGroup(builder, context)
+ }
+}
+
+private fun Element.parsePath(builder: ImageVector.Builder) {
+ builder.addPath(
+ pathData = addPathNodes(attributeOrNull(ANDROID_NS, "pathData")),
+ pathFillType = attributeOrNull(ANDROID_NS, "fillType")
+ ?.let(::parseFillType) ?: PathFillType.NonZero,
+ name = attributeOrNull(ANDROID_NS, "name") ?: "",
+ fill = attributeOrNull(ANDROID_NS, "fillColor")?.let(::parseStringBrush)
+ ?: apptAttr(ANDROID_NS, "fillColor")?.let(Element::parseElementBrush),
+ fillAlpha = attributeOrNull(ANDROID_NS, "fillAlpha")?.toFloat() ?: 1.0f,
+ stroke = attributeOrNull(ANDROID_NS, "strokeColor")?.let(::parseStringBrush)
+ ?: apptAttr(ANDROID_NS, "strokeColor")?.let(Element::parseElementBrush),
+ strokeAlpha = attributeOrNull(ANDROID_NS, "strokeAlpha")?.toFloat() ?: 1.0f,
+ strokeLineWidth = attributeOrNull(ANDROID_NS, "strokeWidth")?.toFloat() ?: 1.0f,
+ strokeLineCap = attributeOrNull(ANDROID_NS, "strokeLineCap")
+ ?.let(::parseStrokeCap) ?: StrokeCap.Butt,
+ strokeLineJoin = attributeOrNull(ANDROID_NS, "strokeLineJoin")
+ ?.let(::parseStrokeJoin) ?: StrokeJoin.Miter,
+ strokeLineMiter = attributeOrNull(ANDROID_NS, "strokeMiterLimit")?.toFloat() ?: 1.0f,
+ trimPathStart = attributeOrNull(ANDROID_NS, "trimPathStart")?.toFloat() ?: 0.0f,
+ trimPathEnd = attributeOrNull(ANDROID_NS, "trimPathEnd")?.toFloat() ?: 1.0f,
+ trimPathOffset = attributeOrNull(ANDROID_NS, "trimPathOffset")?.toFloat() ?: 0.0f
+ )
+}
+
+private fun Element.parseClipPath(builder: ImageVector.Builder, context: BuildContext) {
+ builder.addGroup(
+ name = attributeOrNull(ANDROID_NS, "name") ?: "",
+ clipPathData = addPathNodes(attributeOrNull(ANDROID_NS, "pathData"))
+ )
+ context.currentGroups.add(BuildContext.Group.Virtual)
+}
+
+private fun Element.parseGroup(builder: ImageVector.Builder, context: BuildContext) {
+ builder.addGroup(
+ attributeOrNull(ANDROID_NS, "name") ?: "",
+ attributeOrNull(ANDROID_NS, "rotation")?.toFloat() ?: DefaultRotation,
+ attributeOrNull(ANDROID_NS, "pivotX")?.toFloat() ?: DefaultPivotX,
+ attributeOrNull(ANDROID_NS, "pivotY")?.toFloat() ?: DefaultPivotY,
+ attributeOrNull(ANDROID_NS, "scaleX")?.toFloat() ?: DefaultScaleX,
+ attributeOrNull(ANDROID_NS, "scaleY")?.toFloat() ?: DefaultScaleY,
+ attributeOrNull(ANDROID_NS, "translateX")?.toFloat() ?: DefaultTranslationX,
+ attributeOrNull(ANDROID_NS, "translateY")?.toFloat() ?: DefaultTranslationY,
+ EmptyPath
+ )
+ context.currentGroups.add(BuildContext.Group.Real)
+
+ parseVectorNodes(builder, context)
+
+ do {
+ val removedGroup = context.currentGroups.removeLastOrNull()
+ builder.clearGroup()
+ } while (removedGroup == BuildContext.Group.Virtual)
+}
+
+private fun parseStringBrush(str: String) = SolidColor(Color(parseColorValue(str)))
+
+private fun Element.parseElementBrush(): Brush? =
+ childrenSequence
+ .filterIsElement()
+ .find { it.nodeName == "gradient" }
+ ?.parseGradient()
+
+private fun Element.parseGradient(): Brush? {
+ return when (attributeOrNull(ANDROID_NS, "type")) {
+ "linear" -> parseLinearGradient()
+ "radial" -> parseRadialGradient()
+ "sweep" -> parseSweepGradient()
+ else -> null
+ }
+}
+
+@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
+private fun Element.parseLinearGradient() = Brush.linearGradient(
+ colorStops = parseColorStops(),
+ start = Offset(
+ attributeOrNull(ANDROID_NS, "startX")?.toFloat() ?: 0f,
+ attributeOrNull(ANDROID_NS, "startY")?.toFloat() ?: 0f
+ ),
+ end = Offset(
+ attributeOrNull(ANDROID_NS, "endX")?.toFloat() ?: 0f,
+ attributeOrNull(ANDROID_NS, "endY")?.toFloat() ?: 0f
+ ),
+ tileMode = attributeOrNull(ANDROID_NS, "tileMode")?.let(::parseTileMode) ?: TileMode.Clamp
+)
+
+@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
+private fun Element.parseRadialGradient() = Brush.radialGradient(
+ colorStops = parseColorStops(),
+ center = Offset(
+ attributeOrNull(ANDROID_NS, "centerX")?.toFloat() ?: 0f,
+ attributeOrNull(ANDROID_NS, "centerY")?.toFloat() ?: 0f
+ ),
+ radius = attributeOrNull(ANDROID_NS, "gradientRadius")?.toFloat() ?: 0f,
+ tileMode = attributeOrNull(ANDROID_NS, "tileMode")?.let(::parseTileMode) ?: TileMode.Clamp
+)
+
+@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
+private fun Element.parseSweepGradient() = Brush.sweepGradient(
+ colorStops = parseColorStops(),
+ center = Offset(
+ attributeOrNull(ANDROID_NS, "centerX")?.toFloat() ?: 0f,
+ attributeOrNull(ANDROID_NS, "centerY")?.toFloat() ?: 0f,
+ )
+)
+
+private fun Element.parseColorStops(): Array> {
+ val items = childrenSequence
+ .filterIsElement()
+ .filter { it.nodeName == "item" }
+ .toList()
+
+ val colorStops = items.mapIndexedNotNullTo(mutableListOf()) { index, item ->
+ item.parseColorStop(defaultOffset = index.toFloat() / items.lastIndex.coerceAtLeast(1))
+ }
+
+ if (colorStops.isEmpty()) {
+ val startColor = attributeOrNull(ANDROID_NS, "startColor")?.let(::parseColorValue)
+ val centerColor = attributeOrNull(ANDROID_NS, "centerColor")?.let(::parseColorValue)
+ val endColor = attributeOrNull(ANDROID_NS, "endColor")?.let(::parseColorValue)
+
+ if (startColor != null) {
+ colorStops.add(0f to Color(startColor))
+ }
+ if (centerColor != null) {
+ colorStops.add(0.5f to Color(centerColor))
+ }
+ if (endColor != null) {
+ colorStops.add(1f to Color(endColor))
+ }
+ }
+
+ return colorStops.toTypedArray()
+}
+
+private fun Element.parseColorStop(defaultOffset: Float): Pair? {
+ val offset = attributeOrNull(ANDROID_NS, "offset")?.toFloat() ?: defaultOffset
+ val color = attributeOrNull(ANDROID_NS, "color")?.let(::parseColorValue) ?: return null
+ return offset to Color(color)
+}
+
+/***
+ * Accessing by namespace does not appear to be working with https://github.com/pdvrieze/xmlutil v0.86.0
+ * note: on native ":" had to be prepended to name to load attributes 🤷, js works fine without prepending ":"
+ * todo: figure out how to handle namespaces
+ */
+internal expect fun Element.attributeOrNull(@Suppress("UNUSED_PARAMETER") namespace: String, name: String): String?
+
+/**
+ * Attribute of an element can be represented as a separate child:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * instead of:
+ *
+ *
+ */
+private fun Element.apptAttr(
+ namespace: String,
+ name: String
+): Element? {
+ val prefix = lookupPrefix(namespace) ?: return null
+ return childrenSequence
+ .filterIsElement()
+ .find {
+ it.namespaceURI == AAPT_NS && it.localName == "attr" &&
+ it.getAttribute("name") == "$prefix:$name"
+ }
+}
+
+private val Element.childrenSequence
+ get() = sequence {
+ for (childNode in childNodes.iterator()) {
+ yield(childNode)
+ }
+ }
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/filterIsElement.kt b/kamel-image/src/nonJvmMain/kotlin/filterIsElement.kt
new file mode 100644
index 00000000..4e59eeca
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/filterIsElement.kt
@@ -0,0 +1,4 @@
+import nl.adaptivity.xmlutil.dom.Element
+import nl.adaptivity.xmlutil.dom.Node
+
+internal expect fun Sequence.filterIsElement(): Sequence
diff --git a/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/config/KamelConfig.kt b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/config/KamelConfig.kt
new file mode 100644
index 00000000..f31b3c1f
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/config/KamelConfig.kt
@@ -0,0 +1,9 @@
+package io.kamel.image.config
+
+import io.kamel.core.config.KamelConfigBuilder
+import io.kamel.image.decoder.ImageVectorDecoder
+import io.kamel.image.decoder.SvgDecoder
+
+public fun KamelConfigBuilder.imageVectorDecoder(): Unit = decoder(ImageVectorDecoder)
+
+public fun KamelConfigBuilder.svgDecoder(): Unit = decoder(SvgDecoder)
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/ImageBitmapDecoder.kt b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/ImageBitmapDecoder.kt
new file mode 100644
index 00000000..5d2c6c6f
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/ImageBitmapDecoder.kt
@@ -0,0 +1,24 @@
+package io.kamel.image.decoder
+
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.toComposeImageBitmap
+import io.kamel.core.config.ResourceConfig
+import io.kamel.core.decoder.Decoder
+import io.ktor.util.*
+import io.ktor.utils.io.*
+import org.jetbrains.skia.Image
+import kotlin.reflect.KClass
+
+/**
+ * Decodes and transfers [ByteReadChannel] to [ImageBitmap] using Skia [Image].
+ */
+internal actual object ImageBitmapDecoder : Decoder {
+
+ override val outputKClass: KClass = ImageBitmap::class
+
+ override suspend fun decode(
+ channel: ByteReadChannel,
+ resourceConfig: ResourceConfig
+ ): ImageBitmap = Image.makeFromEncoded(channel.toByteArray()).toComposeImageBitmap()
+
+}
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/ImageVectorDecoder.kt b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/ImageVectorDecoder.kt
new file mode 100644
index 00000000..b9d809b3
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/ImageVectorDecoder.kt
@@ -0,0 +1,20 @@
+package io.kamel.image.decoder
+
+import androidx.compose.ui.graphics.vector.ImageVector
+//import androidx.compose.ui.res.loadXmlImageVector
+import io.kamel.core.config.ResourceConfig
+import io.kamel.core.decoder.Decoder
+import io.ktor.util.*
+import io.ktor.utils.io.*
+import loadXmlImageVector
+import kotlin.reflect.KClass
+
+internal object ImageVectorDecoder : Decoder {
+
+ override val outputKClass: KClass = ImageVector::class
+
+ override suspend fun decode(channel: ByteReadChannel, resourceConfig: ResourceConfig): ImageVector {
+ val xml = channel.toByteArray().decodeToString()
+ return loadXmlImageVector(xml, resourceConfig.density)
+ }
+}
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/SvgDecoder.kt b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/SvgDecoder.kt
new file mode 100644
index 00000000..737afcfb
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/io/kamel/image/decoder/SvgDecoder.kt
@@ -0,0 +1,22 @@
+package io.kamel.image.decoder
+
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.res.loadSvgPainter
+import io.kamel.core.config.ResourceConfig
+import io.kamel.core.decoder.Decoder
+import io.ktor.util.*
+import io.ktor.utils.io.*
+import kotlin.reflect.KClass
+
+
+internal object SvgDecoder : Decoder {
+
+ override val outputKClass: KClass = Painter::class
+
+ override suspend fun decode(channel: ByteReadChannel, resourceConfig: ResourceConfig): Painter {
+ return loadSvgPainter(
+ channel.toByteArray(),
+ resourceConfig.density
+ )
+ }
+}
diff --git a/kamel-image/src/nonJvmMain/kotlin/loadSvgPainter.kt b/kamel-image/src/nonJvmMain/kotlin/loadSvgPainter.kt
new file mode 100644
index 00000000..7204d8d0
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/loadSvgPainter.kt
@@ -0,0 +1,113 @@
+package androidx.compose.ui.res
+
+import DrawCache
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.nativeCanvas
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import io.ktor.util.*
+import io.ktor.utils.io.*
+import org.jetbrains.skia.Data
+import org.jetbrains.skia.Rect
+import org.jetbrains.skia.svg.*
+import kotlin.math.ceil
+
+/**
+ * Synchronously load an SVG image from some [inputStream].
+ *
+ * In contrast to [svgResource] this function isn't [Composable]
+ *
+ * @param inputStream input stream to load an SVG resource. All bytes will be read from this stream,
+ * but stream will not be closed after this method.
+ * @param density density that will be used to set the intrinsic size of the Painter. If the image
+ * will be drawn with the specified size, density will have no effect.
+ * @return the decoded SVG image associated with the resource
+ */
+// Note: copied from here:
+// https://github.com/JetBrains/compose-multiplatform-core/blob/5c26b7b9f5619ee4f319c6caf43192851b8ee15e/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/DesktopSvgResources.desktop.kt#L51
+// todo: remove when available in common androidx
+internal fun loadSvgPainter(
+ bytes: ByteArray,
+ density: Density
+): Painter {
+ val data = Data.makeFromBytes(bytes)
+ return SVGPainter(SVGDOM(data), density)
+}
+
+private class SVGPainter(
+ private val dom: SVGDOM,
+ private val density: Density
+) : Painter() {
+ private val root = dom.root
+
+ private val defaultSizePx: Size = run {
+ val width = root?.width?.withUnit(SVGLengthUnit.PX)?.value ?: 0f
+ val height = root?.height?.withUnit(SVGLengthUnit.PX)?.value ?: 0f
+ if (width == 0f && height == 0f) {
+ Size.Unspecified
+ } else {
+ Size(width, height)
+ }
+ }
+
+ init {
+ if (root?.viewBox == null && defaultSizePx.isSpecified) {
+ root?.viewBox = Rect.makeXYWH(0f, 0f, defaultSizePx.width, defaultSizePx.height)
+ }
+ }
+
+ override val intrinsicSize: Size
+ get() {
+ return if (defaultSizePx.isSpecified) {
+ defaultSizePx * density.density
+ } else {
+ Size.Unspecified
+ }
+ }
+
+ private var previousDrawSize: Size = Size.Unspecified
+ private var alpha: Float = 1.0f
+ private var colorFilter: ColorFilter? = null
+
+ // with caching into bitmap FPS is 3x-4x higher (tested with idea-logo.svg with 30x30 icons)
+ private val drawCache = DrawCache()
+
+ override fun applyAlpha(alpha: Float): Boolean {
+ this.alpha = alpha
+ return true
+ }
+
+ override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
+ this.colorFilter = colorFilter
+ return true
+ }
+
+ override fun DrawScope.onDraw() {
+ if (previousDrawSize != size) {
+ drawCache.drawCachedImage(
+ IntSize(ceil(size.width).toInt(), ceil(size.height).toInt()),
+ density = this,
+ layoutDirection,
+ ) {
+ drawSvg(size)
+ }
+ }
+
+ drawCache.drawInto(this, alpha, colorFilter)
+ }
+
+ private fun DrawScope.drawSvg(size: Size) {
+ drawIntoCanvas { canvas ->
+ root?.width = SVGLength(size.width, SVGLengthUnit.PX)
+ root?.height = SVGLength(size.height, SVGLengthUnit.PX)
+ root?.preserveAspectRatio = SVGPreserveAspectRatio(SVGPreserveAspectRatioAlign.NONE)
+ dom.render(canvas.nativeCanvas)
+ }
+ }
+}
\ No newline at end of file
diff --git a/kamel-image/src/nonJvmMain/kotlin/loadXmlImageVector.kt b/kamel-image/src/nonJvmMain/kotlin/loadXmlImageVector.kt
new file mode 100644
index 00000000..41a53bd9
--- /dev/null
+++ b/kamel-image/src/nonJvmMain/kotlin/loadXmlImageVector.kt
@@ -0,0 +1,27 @@
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.Density
+import nl.adaptivity.xmlutil.dom.Element
+import nl.adaptivity.xmlutil.serialization.ElementSerializer
+import nl.adaptivity.xmlutil.serialization.XML
+
+/**
+ * Synchronously load an xml vector image from some [xmlString].
+ *
+ * XML Vector Image came from Android world. See:
+ * https://developer.android.com/guide/topics/graphics/vector-drawable-resources
+ *
+ * On desktop it is fully implemented except there is no resource linking
+ * (for example, we can't reference to color defined in another file)
+ *
+ * @param xmlString input xml vector image string.
+ * @param density density that will be used to set the default size of the ImageVector. If the image
+ * will be drawn with the specified size, density will have no effect.
+ * @return the decoded vector image associated with the image
+ */
+internal fun loadXmlImageVector(
+ xmlString: String,
+ density: Density
+): ImageVector {
+ val element: Element = XML.decodeFromString(ElementSerializer, xmlString)
+ return element.parseVectorRoot(density)
+}
\ No newline at end of file
diff --git a/kamel-samples/build.gradle.kts b/kamel-samples/build.gradle.kts
index 37b43c98..19ee563c 100644
--- a/kamel-samples/build.gradle.kts
+++ b/kamel-samples/build.gradle.kts
@@ -1,9 +1,19 @@
+import org.jetbrains.compose.desktop.application.tasks.AbstractNativeMacApplicationPackageAppDirTask
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
+import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractExecutable
+import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary
+import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
+import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
+import java.io.File
+import java.io.FileFilter
+import org.jetbrains.compose.experimental.dsl.IOSDevices as IOSDevices1
+import org.jetbrains.kotlin.konan.file.File as KonanFile
plugins {
multiplatform
compose
`android-application`
+ mokoResources
}
android {
@@ -18,8 +28,8 @@ android {
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
packagingOptions {
@@ -27,42 +37,49 @@ android {
excludes += setOf("META-INF/AL2.0", "META-INF/LGPL2.1")
}
}
-
- sourceSets {
- named("main") {
- manifest.srcFile("src/androidMain/AndroidManifest.xml")
- res.srcDirs("src/androidMain/res", "src/commonMain/resources")
- }
- }
-
- configurations {
- create("androidTestApi")
- create("androidTestDebugApi")
- create("androidTestReleaseApi")
- create("testApi")
- create("testDebugApi")
- create("testReleaseApi")
- named("implementation") {
- exclude(group = "androidx.compose.animation")
- exclude(group = "androidx.compose.foundation")
- exclude(group = "androidx.compose.material")
- exclude(group = "androidx.compose.runtime")
- exclude(group = "androidx.compose.ui")
- }
- }
}
kotlin {
explicitApi = ExplicitApiMode.Warning
+ jvmToolchain {
+ languageVersion.set(JavaLanguageVersion.of("11"))
+ }
+
android()
- jvm("desktop") {
- compilations.all {
- kotlinOptions {
- jvmTarget = "11"
+ jvm("desktop")
+ js(IR) {
+ browser()
+ binaries.executable()
+ }
+ for (target in Targets.macosTargets) {
+ targets.add(
+ (presets.getByName(target)
+ .createTarget(target) as org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget).apply {
+ binaries.executable {
+ freeCompilerArgs += listOf(
+ "-linker-option", "-framework", "-linker-option", "Metal"
+ )
+ }
}
- }
+ )
+ }
+ for (target in Targets.iosTargets) {
+ targets.add(
+ (presets.getByName(target)
+ .createTarget(target.replace("ios", "uikit"))
+ as org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget).apply {
+ binaries.executable {
+ entryPoint = "main"
+ freeCompilerArgs += listOf(
+ "-linker-option", "-framework", "-linker-option", "Metal",
+ "-linker-option", "-framework", "-linker-option", "CoreText",
+ "-linker-option", "-framework", "-linker-option", "CoreGraphics"
+ )
+ }
+ }
+ )
}
sourceSets {
@@ -71,15 +88,15 @@ kotlin {
dependencies {
implementation(project(":kamel-image"))
implementation(project(":kamel-tests"))
+ implementation(compose.foundation)
implementation(compose.material)
- implementation(compose.animation)
}
}
val androidMain by getting {
+ dependsOn(commonMain)
dependencies {
implementation(Dependencies.Android.Appcompat)
- implementation(Dependencies.Android.Core)
implementation(Dependencies.Android.ActivityCompose)
implementation(Dependencies.Android.Material)
implementation(Dependencies.Ktor.Android)
@@ -87,34 +104,177 @@ kotlin {
}
val desktopMain by getting {
+ dependsOn(commonMain)
dependencies {
implementation(compose.desktop.currentOs)
implementation(Dependencies.Ktor.CIO)
}
}
- all {
- languageSettings.apply {
- optIn("kotlin.Experimental")
+ val darwinMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val macosMain by creating {
+ dependsOn(darwinMain)
+ }
+
+ Targets.macosTargets.forEach { target ->
+ getByName("${target}Main") {
+ dependsOn(macosMain)
}
}
- targets.all {
- compilations.all {
- kotlinOptions {
- freeCompilerArgs =
- listOf("-Xopt-in=kotlin.RequiresOptIn")
- }
+ val uikitMain by creating {
+ dependsOn(darwinMain)
+ }
+
+ Targets.iosTargets.map { target ->
+ target.replace("ios", "uikit")
+ }.forEach { target ->
+ getByName("${target}Main") {
+ dependsOn(uikitMain)
+ }
+ }
+
+ all {
+ languageSettings.apply {
+ optIn("kotlin.Experimental")
}
}
}
}
+
+multiplatformResources {
+ multiplatformResourcesPackage = "io.kamel.samples"
+}
+
+
compose {
desktop {
application {
mainClass = "io.kamel.samples.DesktopSampleKt"
}
}
-}
\ No newline at end of file
+}
+
+compose.experimental {
+ web.application {}
+ uikit.application {
+ bundleIdPrefix = "io.kamel.samples"
+ projectName = "kamel samples"
+ deployConfigurations {
+ simulator("IPhone13") {
+ //Usage: ./gradlew iosDeployIPhone8Debug
+ device = IOSDevices1.IPHONE_13
+ }
+ simulator("IPad") {
+ //Usage: ./gradlew iosDeployIPadDebug
+ device = IOSDevices1.IPAD_PRO_11_INCH_3rd_Gen
+ }
+ connectedDevice("Device") {
+ //First need specify your teamId here, or in local.properties (compose.ios.teamId=***)
+ //teamId="***"
+ //Usage: ./gradlew iosDeployDeviceRelease
+ }
+ }
+ }
+}
+
+
+compose.desktop.nativeApplication {
+ targets(kotlin.targets.getByName("macosArm64"))
+ distributions {
+ targetFormats(org.jetbrains.compose.desktop.application.dsl.TargetFormat.Dmg)
+ packageName = "Native-Sample"
+ packageVersion = "1.0.0"
+ }
+}
+
+// TODO: remove when https://youtrack.jetbrains.com/issue/KT-50778 fixed
+project.tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile::class.java).configureEach {
+ kotlinOptions.freeCompilerArgs += listOf(
+ "-Xir-dce-runtime-diagnostic=log"
+ )
+}
+
+// todo: remove after https://github.com/icerockdev/moko-resources/issues/392 resolved
+// copy resources from kamel-tests into the proper directory for kamel-samples so they are packaged for
+// the web app
+tasks.register("jsCopyResourcesFromKamelTests") {
+ from("../kamel-tests/build/generated/moko/jsMain/iokameltests/res")
+ into("build/generated/moko/jsMain/iokamelsamples/res")
+ dependsOn(":kamel-tests:generateMRjsMain")
+}
+tasks.getByName("jsProcessResources").dependsOn("jsCopyResourcesFromKamelTests")
+
+tasks.register("desktopCopyResourcesFromKamelTests") {
+ from("../kamel-tests/build/generated/moko/jvmMain/iokameltests/res")
+ into("build/generated/moko/desktopMain/iokamelsamples/res")
+ dependsOn(":kamel-tests:generateMRjvmMain")
+}
+tasks.getByName("desktopProcessResources").dependsOn("desktopCopyResourcesFromKamelTests")
+
+// todo: Remove when resolved: https://github.com/icerockdev/moko-resources/issues/372
+tasks.withType()
+ .matching { linkTask -> linkTask.binary is AbstractExecutable }
+ .configureEach {
+ val task: KotlinNativeLink = this
+
+ doLast {
+ val binary: NativeBinary = task.binary
+ val outputDir: File = task.outputFile.get().parentFile
+ task.libraries
+ .filter { library -> library.extension == "klib" }
+ .filter(File::exists)
+ .forEach { inputFile ->
+ val klibKonan = KonanFile(inputFile.path)
+ val klib = KotlinLibraryLayoutImpl(
+ klib = klibKonan,
+ component = "default"
+ )
+ val layout = klib.extractingToTemp
+
+ // extracting bundles
+ layout
+ .resourcesDir
+ .absolutePath
+ .let(::File)
+ .listFiles(FileFilter { it.extension == "bundle" })
+ // copying bundles to app
+ ?.forEach { bundleFile ->
+ logger.info("${bundleFile.absolutePath} copying to $outputDir")
+ bundleFile.copyRecursively(
+ target = File(outputDir, bundleFile.name),
+ overwrite = true
+ )
+ }
+ }
+ }
+ }
+
+tasks.withType {
+ val task: AbstractNativeMacApplicationPackageAppDirTask = this
+
+ doLast {
+ val execFile: File = task.executable.get().asFile
+ val execDir: File = execFile.parentFile
+ val destDir: File = task.destinationDir.asFile.get()
+ val bundleID: String = task.bundleID.get()
+
+ val outputDir = File(destDir, "$bundleID.app/Contents/Resources")
+ outputDir.mkdirs()
+
+ execDir.listFiles().orEmpty()
+ .filter { it.extension == "bundle" }
+ .forEach { bundleFile ->
+ logger.info("${bundleFile.absolutePath} copying to $outputDir")
+ bundleFile.copyRecursively(
+ target = File(outputDir, bundleFile.name),
+ overwrite = true
+ )
+ }
+ }
+}
diff --git a/kamel-samples/src/androidMain/kotlin/io/kamel/samples/AndroidSample.kt b/kamel-samples/src/androidMain/kotlin/io/kamel/samples/AndroidSample.kt
index 092aa949..3f0e4d64 100644
--- a/kamel-samples/src/androidMain/kotlin/io/kamel/samples/AndroidSample.kt
+++ b/kamel-samples/src/androidMain/kotlin/io/kamel/samples/AndroidSample.kt
@@ -3,6 +3,12 @@ package io.kamel.samples
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.runtime.remember
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.config.takeFrom
+import io.kamel.image.config.Default
+import io.kamel.image.config.imageBitmapDecoder
+import io.kamel.image.config.resourcesFetcher
public actual val cellsCount: Int = 2
@@ -11,7 +17,16 @@ class AndroidSample : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
- Gallery()
+ val kamelConfig = remember {
+ KamelConfig {
+ takeFrom(KamelConfig.Default)
+ resourcesFetcher(this@AndroidSample)
+// imageVectorDecoder()
+// svgDecsoder()
+ imageBitmapDecoder()
+ }
+ }
+ launcher(kamelConfig, applicationContext)
}
}
diff --git a/kamel-samples/src/androidMain/kotlin/io/kamel/samples/ResourcesSample.kt b/kamel-samples/src/androidMain/kotlin/io/kamel/samples/ResourcesSample.kt
deleted file mode 100644
index e694ccc0..00000000
--- a/kamel-samples/src/androidMain/kotlin/io/kamel/samples/ResourcesSample.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package io.kamel.samples
-
-import android.os.Bundle
-import androidx.activity.compose.setContent
-import androidx.appcompat.app.AppCompatActivity
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import io.kamel.core.config.KamelConfig
-import io.kamel.core.config.takeFrom
-import io.kamel.image.KamelImage
-import io.kamel.image.config.Default
-import io.kamel.image.config.LocalKamelConfig
-import io.kamel.image.config.resourcesFetcher
-import io.kamel.image.config.resourcesIdMapper
-import io.kamel.image.lazyImageResource
-import io.kamel.image.lazyPainterResource
-
-class ResourcesSample : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContent {
-
- val context = LocalContext.current
-
- val androidConfig = KamelConfig {
- takeFrom(KamelConfig.Default)
- resourcesFetcher(context)
- resourcesIdMapper(context)
- }
-
- CompositionLocalProvider(LocalKamelConfig provides androidConfig) {
-
- val imageResource = lazyPainterResource(R.raw.compose)
-
- KamelImage(
- resource = imageResource,
- contentDescription = "Compose",
- modifier = Modifier.fillMaxSize(),
- )
-
- }
-
- }
- }
-}
\ No newline at end of file
diff --git a/kamel-samples/src/androidMain/kotlin/io/kamel/samples/getFile.kt b/kamel-samples/src/androidMain/kotlin/io/kamel/samples/getFile.kt
new file mode 100644
index 00000000..ebb6bb49
--- /dev/null
+++ b/kamel-samples/src/androidMain/kotlin/io/kamel/samples/getFile.kt
@@ -0,0 +1,29 @@
+package io.kamel.samples
+
+import android.content.Context
+import android.content.res.Resources
+import android.util.Log
+import android.util.TypedValue
+import dev.icerock.moko.resources.FileResource
+import io.kamel.core.utils.File
+import java.io.FileOutputStream
+import java.io.InputStream
+
+
+public actual suspend fun getFile(fileResource: FileResource, context: Any?): File {
+ val resources: Resources = (context as Context).resources
+ val value = TypedValue()
+ resources.getValue(fileResource.rawResId, value, true)
+ val name = value.string.toString()
+ val file = java.io.File.createTempFile("temp", ".${name.substringAfterLast(".")}")
+ val ins: InputStream = resources.openRawResource(fileResource.rawResId)
+ FileOutputStream(file).use { os ->
+ val buffer = ByteArray(4096)
+ var length: Int
+ while (ins.read(buffer).also { length = it } > 0) {
+ os.write(buffer, 0, length)
+ }
+ os.flush()
+ }
+ return file
+}
\ No newline at end of file
diff --git a/kamel-samples/src/commonMain/kotlin/io/kamel/samples/FileSample.kt b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/FileSample.kt
new file mode 100644
index 00000000..c82e2fc8
--- /dev/null
+++ b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/FileSample.kt
@@ -0,0 +1,38 @@
+package io.kamel.samples
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import dev.icerock.moko.resources.FileResource
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.utils.File
+import io.kamel.image.KamelImage
+import io.kamel.image.config.LocalKamelConfig
+import io.kamel.image.lazyPainterResource
+import kotlinx.coroutines.launch
+
+
+@Composable
+internal fun FileSample(fileResource: FileResource, kamelConfig: KamelConfig, context: Any? = null) {
+
+ val scope = rememberCoroutineScope()
+ var file: File? by remember { mutableStateOf(null) }
+
+ scope.launch {
+ file = getFile(fileResource, context)
+ }
+
+
+ CompositionLocalProvider(LocalKamelConfig provides kamelConfig) {
+ Column {
+ file?.let { file ->
+ val painterResource = lazyPainterResource(file)
+ KamelImage(painterResource,
+ contentDescription = "Compose",
+ modifier = Modifier.fillMaxSize(),
+ onFailure = { throw it })
+ }
+ }
+ }
+}
diff --git a/kamel-samples/src/commonMain/kotlin/io/kamel/samples/Gallery.kt b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/Gallery.kt
index 8f524d72..5e61ba28 100644
--- a/kamel-samples/src/commonMain/kotlin/io/kamel/samples/Gallery.kt
+++ b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/Gallery.kt
@@ -50,7 +50,7 @@ public fun Gallery() {
contentDescription = null,
modifier = Modifier
.aspectRatio(1F)
- .padding(16.dp)
+ .padding(8.dp)
.shadow(elevation = 8.dp, RoundedCornerShape(16.dp))
.background(Color.White, RoundedCornerShape(16.dp))
.clip(RoundedCornerShape(16.dp)),
diff --git a/kamel-samples/src/commonMain/kotlin/io/kamel/samples/getFile.kt b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/getFile.kt
new file mode 100644
index 00000000..63930b15
--- /dev/null
+++ b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/getFile.kt
@@ -0,0 +1,7 @@
+package io.kamel.samples
+
+import dev.icerock.moko.resources.AssetResource
+import dev.icerock.moko.resources.FileResource
+import io.kamel.core.utils.File
+
+public expect suspend fun getFile(fileResource: FileResource, context: Any? = null): File
\ No newline at end of file
diff --git a/kamel-samples/src/commonMain/kotlin/io/kamel/samples/launcher.kt b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/launcher.kt
new file mode 100644
index 00000000..06f20b97
--- /dev/null
+++ b/kamel-samples/src/commonMain/kotlin/io/kamel/samples/launcher.kt
@@ -0,0 +1,49 @@
+package io.kamel.samples
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import io.kamel.core.config.KamelConfig
+import io.kamel.tests.MR
+
+@androidx.compose.runtime.Composable
+public fun launcher(kamelConfig: KamelConfig, context: Any? = null) {
+ var sampleIndex by remember { mutableStateOf(0) }
+ Column {
+ Row {
+ Button({
+ sampleIndex = 0
+ }) {
+ Text("Gallery")
+ }
+ Button({
+ sampleIndex = 1
+ }) {
+ Text("Bitmap")
+ }
+ Button({
+ sampleIndex = 2
+ }) {
+ Text("Svg")
+ }
+ Button({
+ sampleIndex = 3
+ }) {
+ Text("Xml")
+ }
+ }
+ when (sampleIndex) {
+ 0 -> Gallery()
+ 1 -> FileSample(MR.files.Compose, kamelConfig, context)
+ 2 -> FileSample(MR.files.Kotlin, kamelConfig, context)
+ 3 -> FileSample(MR.files.ComposeXml, kamelConfig, context)
+
+ else -> Text("Invalid Sample Index")
+ }
+ }
+}
\ No newline at end of file
diff --git a/kamel-samples/src/darwinMain/kotlin/io/kamel/samples/darwinCellsCount.kt b/kamel-samples/src/darwinMain/kotlin/io/kamel/samples/darwinCellsCount.kt
new file mode 100644
index 00000000..f2b4dfd8
--- /dev/null
+++ b/kamel-samples/src/darwinMain/kotlin/io/kamel/samples/darwinCellsCount.kt
@@ -0,0 +1,4 @@
+package io.kamel.samples
+
+
+public actual val cellsCount: Int = 4
diff --git a/kamel-samples/src/darwinMain/kotlin/io/kamel/samples/getFile.kt b/kamel-samples/src/darwinMain/kotlin/io/kamel/samples/getFile.kt
new file mode 100644
index 00000000..b9c210a8
--- /dev/null
+++ b/kamel-samples/src/darwinMain/kotlin/io/kamel/samples/getFile.kt
@@ -0,0 +1,9 @@
+package io.kamel.samples
+
+import dev.icerock.moko.resources.FileResource
+import io.kamel.core.utils.File
+import io.kamel.tests.MR
+
+public actual suspend fun getFile(fileResource: FileResource, context: Any?): File {
+ return File(fileResource.path)
+}
\ No newline at end of file
diff --git a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/DesktopSample.kt b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/DesktopSample.kt
index fcad554d..871c8b87 100644
--- a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/DesktopSample.kt
+++ b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/DesktopSample.kt
@@ -1,7 +1,22 @@
package io.kamel.samples
+import androidx.compose.runtime.remember
import androidx.compose.ui.window.singleWindowApplication
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.config.takeFrom
+import io.kamel.image.config.*
public actual val cellsCount: Int = 4
-public fun main(): Unit = singleWindowApplication { Gallery() }
\ No newline at end of file
+public fun main(): Unit = singleWindowApplication {
+ val kamelConfig = remember {
+ KamelConfig {
+ takeFrom(KamelConfig.Default)
+ resourcesFetcher()
+ imageVectorDecoder()
+ svgDecoder()
+ imageBitmapDecoder()
+ }
+ }
+ launcher(kamelConfig)
+}
\ No newline at end of file
diff --git a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/FileSample.kt b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/FileSample.kt
deleted file mode 100644
index 50f7f259..00000000
--- a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/FileSample.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.kamel.samples
-
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.window.singleWindowApplication
-import io.kamel.core.config.KamelConfig
-import io.kamel.core.config.takeFrom
-import io.kamel.image.KamelImage
-import io.kamel.image.config.Default
-import io.kamel.image.config.LocalKamelConfig
-import io.kamel.image.config.imageVectorDecoder
-import io.kamel.image.config.resourcesFetcher
-import io.kamel.image.lazyPainterResource
-import java.io.File
-
-public fun main(): Unit = singleWindowApplication {
- val kamelConfig = KamelConfig {
- takeFrom(KamelConfig.Default)
- resourcesFetcher()
- imageVectorDecoder()
- }
-
- CompositionLocalProvider(LocalKamelConfig provides kamelConfig) {
- val painterResource =
- lazyPainterResource(File("kamel-tests/src/commonMain/resources/Compose.png"))
-
- KamelImage(
- painterResource,
- contentDescription = "Compose",
- modifier = Modifier.fillMaxSize(),
- onFailure = { throw it }
- )
- }
-}
\ No newline at end of file
diff --git a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/ResourcesSample.kt b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/ResourcesSample.kt
index 43c2b782..5b020d63 100644
--- a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/ResourcesSample.kt
+++ b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/ResourcesSample.kt
@@ -19,7 +19,7 @@ public fun main(): Unit = singleWindowApplication {
}
CompositionLocalProvider(LocalKamelConfig provides kamelConfig) {
- val painterResource = lazyPainterResource("Compose.png")
+ val painterResource = lazyPainterResource("MR/files/Compose.png")
KamelImage(
painterResource,
diff --git a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/SvgSample.kt b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/SvgSample.kt
index e527bdf5..0b5db5f8 100644
--- a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/SvgSample.kt
+++ b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/SvgSample.kt
@@ -21,7 +21,7 @@ public fun main(): Unit = singleWindowApplication {
}
CompositionLocalProvider(LocalKamelConfig provides kamelConfig) {
- val painterResource = lazyPainterResource("Kotlin.svg")
+ val painterResource = lazyPainterResource("MR/files/Kotlin.svg")
KamelImage(
painterResource,
diff --git a/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/getFile.kt b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/getFile.kt
new file mode 100644
index 00000000..8ea6d9ea
--- /dev/null
+++ b/kamel-samples/src/desktopMain/kotlin/io/kamel/samples/getFile.kt
@@ -0,0 +1,20 @@
+package io.kamel.samples
+
+import dev.icerock.moko.resources.FileResource
+import io.kamel.core.utils.File
+import java.io.FileOutputStream
+
+
+public actual suspend fun getFile(fileResource: FileResource, context: Any?): File {
+ val file = java.io.File.createTempFile("temp", ".${fileResource.filePath.substringAfterLast(".")}")
+ val ins = Thread.currentThread().contextClassLoader.getResource(fileResource.filePath).openStream()
+ FileOutputStream(file).use { os ->
+ val buffer = ByteArray(4096)
+ var length: Int
+ while (ins.read(buffer).also { length = it } > 0) {
+ os.write(buffer, 0, length)
+ }
+ os.flush()
+ }
+ return file
+}
\ No newline at end of file
diff --git a/kamel-samples/src/jsMain/kotlin/io/kamel/samples/cellsCount.kt b/kamel-samples/src/jsMain/kotlin/io/kamel/samples/cellsCount.kt
new file mode 100644
index 00000000..f2b4dfd8
--- /dev/null
+++ b/kamel-samples/src/jsMain/kotlin/io/kamel/samples/cellsCount.kt
@@ -0,0 +1,4 @@
+package io.kamel.samples
+
+
+public actual val cellsCount: Int = 4
diff --git a/kamel-samples/src/jsMain/kotlin/io/kamel/samples/getFile.kt b/kamel-samples/src/jsMain/kotlin/io/kamel/samples/getFile.kt
new file mode 100644
index 00000000..25d0c11a
--- /dev/null
+++ b/kamel-samples/src/jsMain/kotlin/io/kamel/samples/getFile.kt
@@ -0,0 +1,14 @@
+package io.kamel.samples
+
+import dev.icerock.moko.resources.FileResource
+import io.kamel.core.utils.File
+import kotlinx.browser.window
+import kotlinx.coroutines.await
+
+public actual suspend fun getFile(fileResource: FileResource, context: Any?): File {
+ val blob = window.fetch(fileResource.fileUrl).await().blob().await()
+ return File(org.w3c.files.File(
+ arrayOf(blob),
+ fileResource.fileUrl
+ ))
+}
\ No newline at end of file
diff --git a/kamel-samples/src/jsMain/kotlin/main.js.kt b/kamel-samples/src/jsMain/kotlin/main.js.kt
new file mode 100644
index 00000000..2d79a87e
--- /dev/null
+++ b/kamel-samples/src/jsMain/kotlin/main.js.kt
@@ -0,0 +1,20 @@
+import androidx.compose.ui.window.Window
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.config.takeFrom
+import io.kamel.image.config.*
+import io.kamel.samples.launcher
+import org.jetbrains.skiko.wasm.onWasmReady
+
+public fun main() {
+ val kamelConfig = KamelConfig {
+ takeFrom(KamelConfig.Default)
+ imageVectorDecoder()
+ svgDecoder()
+ imageBitmapDecoder()
+ }
+ onWasmReady {
+ Window("Sample") {
+ launcher(kamelConfig)
+ }
+ }
+}
\ No newline at end of file
diff --git a/kamel-samples/src/jsMain/resources/index.html b/kamel-samples/src/jsMain/resources/index.html
new file mode 100644
index 00000000..fd014742
--- /dev/null
+++ b/kamel-samples/src/jsMain/resources/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+ Kamel Samples
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kamel-samples/src/jsMain/resources/styles.css b/kamel-samples/src/jsMain/resources/styles.css
new file mode 100644
index 00000000..e5b3293a
--- /dev/null
+++ b/kamel-samples/src/jsMain/resources/styles.css
@@ -0,0 +1,8 @@
+#root {
+ width: 100%;
+ height: 100vh;
+}
+
+#root > .compose-web-column > div {
+ position: relative;
+}
\ No newline at end of file
diff --git a/kamel-samples/src/macosMain/kotlin/main.macos.kt b/kamel-samples/src/macosMain/kotlin/main.macos.kt
new file mode 100644
index 00000000..bc6db61e
--- /dev/null
+++ b/kamel-samples/src/macosMain/kotlin/main.macos.kt
@@ -0,0 +1,21 @@
+import androidx.compose.ui.window.Window
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.config.takeFrom
+import io.kamel.image.config.*
+import io.kamel.samples.launcher
+import platform.AppKit.NSApp
+import platform.AppKit.NSApplication
+
+public fun main() {
+ NSApplication.sharedApplication()
+ val kamelConfig = KamelConfig {
+ takeFrom(KamelConfig.Default)
+ imageVectorDecoder()
+ svgDecoder()
+ imageBitmapDecoder()
+ }
+ Window("Sample") {
+ launcher(kamelConfig)
+ }
+ NSApp?.run()
+}
\ No newline at end of file
diff --git a/kamel-samples/src/uikitMain/kotlin/main.uikit.kt b/kamel-samples/src/uikitMain/kotlin/main.uikit.kt
new file mode 100644
index 00000000..8fdc59f9
--- /dev/null
+++ b/kamel-samples/src/uikitMain/kotlin/main.uikit.kt
@@ -0,0 +1,59 @@
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.ComposeUIViewController
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.config.takeFrom
+import io.kamel.image.config.Default
+import io.kamel.image.config.imageBitmapDecoder
+import io.kamel.image.config.imageVectorDecoder
+import io.kamel.image.config.svgDecoder
+import io.kamel.samples.launcher
+import kotlinx.cinterop.autoreleasepool
+import kotlinx.cinterop.cstr
+import kotlinx.cinterop.memScoped
+import kotlinx.cinterop.toCValues
+import platform.Foundation.NSStringFromClass
+import platform.UIKit.*
+
+public fun main() {
+ val args = emptyArray()
+ memScoped {
+ val argc = args.size + 1
+ val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues()
+ autoreleasepool {
+ UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate))
+ }
+ }
+}
+
+internal class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol {
+ internal companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta
+
+ @OverrideInit
+ internal constructor() : super()
+
+ private var _window: UIWindow? = null
+ override fun window(): UIWindow? = _window
+ override fun setWindow(window: UIWindow?) {
+ _window = window
+ }
+
+ override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean {
+ window = UIWindow(frame = UIScreen.mainScreen.bounds)
+ val kamelConfig = KamelConfig {
+ takeFrom(KamelConfig.Default)
+ imageVectorDecoder()
+ svgDecoder()
+ imageBitmapDecoder()
+ }
+ window!!.rootViewController = ComposeUIViewController {
+ Column(Modifier.padding(top = 30.dp)) {
+ launcher(kamelConfig)
+ }
+ }
+ window!!.makeKeyAndVisible()
+ return true
+ }
+}
diff --git a/kamel-tests/build.gradle.kts b/kamel-tests/build.gradle.kts
index 2e4ec16d..717eced6 100644
--- a/kamel-tests/build.gradle.kts
+++ b/kamel-tests/build.gradle.kts
@@ -1,23 +1,74 @@
plugins {
+ `android-library`
multiplatform
+ mokoResources
}
kotlin {
jvm()
+ android()
+ js(IR) {
+ browser()
+ }
+ for (target in Targets.nativeTargets) {
+ targets.add(presets.getByName(target).createTarget(target))
+ }
sourceSets {
val commonMain by getting {
dependencies {
implementation(Dependencies.Testing.Ktor)
- implementation(Dependencies.Testing.Compose)
- implementation(Dependencies.Testing.Coroutines)
+ implementation(Dependencies.Coroutines.Core)
+ api(Dependencies.MokoResources.Core)
}
}
val commonTest by getting {
dependencies {
- implementation(kotlin("test-common"))
- implementation(kotlin("test-annotations-common"))
+ implementation(kotlin("test"))
+ api(Dependencies.MokoResources.Test)
}
}
+
+ val darwinMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val darwinTest by creating {
+ dependsOn(commonTest)
+ }
+
+ Targets.darwinTargets.forEach { target ->
+ getByName("${target}Main") {
+ dependsOn(darwinMain)
+ }
+ getByName("${target}Test") {
+ dependsOn(darwinTest)
+ }
+ }
+ }
+}
+
+android {
+ compileSdk = 33
+
+ defaultConfig {
+ minSdk = 16
}
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ defaultConfig {
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ //https://github.com/icerockdev/moko-resources/issues/403
+ sourceSets["main"].res.srcDir(File(buildDir, "generated/moko/androidMain/res"))
+}
+
+
+multiplatformResources {
+ multiplatformResourcesPackage = "io.kamel.tests"
}
\ No newline at end of file
diff --git a/kamel-tests/src/androidMain/AndroidManifest.xml b/kamel-tests/src/androidMain/AndroidManifest.xml
new file mode 100644
index 00000000..17e5f52a
--- /dev/null
+++ b/kamel-tests/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/kamel-tests/src/androidMain/kotlin/io/kamel/tests/AndroidTestUtils.kt b/kamel-tests/src/androidMain/kotlin/io/kamel/tests/AndroidTestUtils.kt
new file mode 100644
index 00000000..f89696e3
--- /dev/null
+++ b/kamel-tests/src/androidMain/kotlin/io/kamel/tests/AndroidTestUtils.kt
@@ -0,0 +1,17 @@
+package io.kamel.tests
+
+import io.ktor.utils.io.*
+import io.ktor.utils.io.core.*
+
+
+actual suspend fun resourceImage(): ByteReadChannel {
+ TODO()
+// val bytes = MR.assets.Compose.readText().encodeToByteArray()
+// return ByteReadChannel(bytes)
+}
+
+actual suspend fun svgImage(): ByteReadChannel {
+ TODO()
+// val bytes = MR.files.Kotlin.readText().encodeToByteArray()
+// return ByteReadChannel(bytes)
+}
diff --git a/kamel-tests/src/commonMain/kotlin/io/kamel/tests/HttpMockEngine.kt b/kamel-tests/src/commonMain/kotlin/io/kamel/tests/HttpMockEngine.kt
index 02cc2925..eb6c65f6 100644
--- a/kamel-tests/src/commonMain/kotlin/io/kamel/tests/HttpMockEngine.kt
+++ b/kamel-tests/src/commonMain/kotlin/io/kamel/tests/HttpMockEngine.kt
@@ -7,13 +7,13 @@ import io.ktor.utils.io.*
val HttpMockEngine = MockEngine { request ->
when (request.url.encodedPath) {
"/emptyImage.jpg" -> respond(ByteReadChannel.Empty)
- "/image.jpg" -> respond(resourceImage)
- "/image.svg" -> respond(svgImage)
+ "/image.jpg" -> respond(resourceImage())
+ "/image.svg" -> respond(svgImage())
else -> respondError(HttpStatusCode.NotFound)
}
}
const val TestStringUrl = "https://www.example.com"
-expect val resourceImage: ByteReadChannel
-expect val svgImage: ByteReadChannel
\ No newline at end of file
+expect suspend fun resourceImage(): ByteReadChannel
+expect suspend fun svgImage(): ByteReadChannel
\ No newline at end of file
diff --git a/kamel-tests/src/commonMain/resources/Compose.png b/kamel-tests/src/commonMain/resources/MR/files/Compose.png
similarity index 100%
rename from kamel-tests/src/commonMain/resources/Compose.png
rename to kamel-tests/src/commonMain/resources/MR/files/Compose.png
diff --git a/kamel-tests/src/commonMain/resources/Compose.xml b/kamel-tests/src/commonMain/resources/MR/files/ComposeXml.xml
similarity index 100%
rename from kamel-tests/src/commonMain/resources/Compose.xml
rename to kamel-tests/src/commonMain/resources/MR/files/ComposeXml.xml
diff --git a/kamel-tests/src/commonMain/resources/Kotlin.svg b/kamel-tests/src/commonMain/resources/MR/files/Kotlin.svg
similarity index 100%
rename from kamel-tests/src/commonMain/resources/Kotlin.svg
rename to kamel-tests/src/commonMain/resources/MR/files/Kotlin.svg
diff --git a/kamel-tests/src/darwinMain/kotlin/io/kamel/tests/JvmTestUtils.kt b/kamel-tests/src/darwinMain/kotlin/io/kamel/tests/JvmTestUtils.kt
new file mode 100644
index 00000000..76f78948
--- /dev/null
+++ b/kamel-tests/src/darwinMain/kotlin/io/kamel/tests/JvmTestUtils.kt
@@ -0,0 +1,15 @@
+package io.kamel.tests
+
+import io.ktor.utils.io.*
+import io.ktor.utils.io.core.*
+
+
+actual suspend fun resourceImage(): ByteReadChannel {
+ val bytes = MR.files.Compose.readText().encodeToByteArray()
+ return ByteReadChannel(bytes)
+}
+
+actual suspend fun svgImage(): ByteReadChannel {
+ val bytes = MR.files.Kotlin.readText().encodeToByteArray()
+ return ByteReadChannel(bytes)
+}
diff --git a/kamel-tests/src/jsMain/kotlin/io/kamel/tests/JsTestUtils.kt b/kamel-tests/src/jsMain/kotlin/io/kamel/tests/JsTestUtils.kt
new file mode 100644
index 00000000..31968b98
--- /dev/null
+++ b/kamel-tests/src/jsMain/kotlin/io/kamel/tests/JsTestUtils.kt
@@ -0,0 +1,13 @@
+package io.kamel.tests
+
+import io.ktor.utils.io.*
+
+actual suspend fun resourceImage(): ByteReadChannel {
+ val bytes = MR.files.Compose.getText().encodeToByteArray()
+ return ByteReadChannel(bytes)
+}
+
+actual suspend fun svgImage(): ByteReadChannel {
+ val bytes = MR.files.Kotlin.getText().encodeToByteArray()
+ return ByteReadChannel(bytes)
+}
diff --git a/kamel-tests/src/jvmMain/kotlin/io/kamel/tests/JvmTestUtils.kt b/kamel-tests/src/jvmMain/kotlin/io/kamel/tests/JvmTestUtils.kt
index f510c80f..0be87582 100644
--- a/kamel-tests/src/jvmMain/kotlin/io/kamel/tests/JvmTestUtils.kt
+++ b/kamel-tests/src/jvmMain/kotlin/io/kamel/tests/JvmTestUtils.kt
@@ -3,16 +3,12 @@ package io.kamel.tests
import io.ktor.utils.io.*
-actual val resourceImage: ByteReadChannel
- get() {
- val url = Thread.currentThread().contextClassLoader.getResource("Compose.png")!!
- val bytes = url.readBytes()
- return ByteReadChannel(bytes)
- }
-
-actual val svgImage: ByteReadChannel
- get() {
- val url = Thread.currentThread().contextClassLoader.getResource("Kotlin.svg")!!
- val bytes = url.readBytes()
- return ByteReadChannel(bytes)
- }
+actual suspend fun resourceImage(): ByteReadChannel {
+ val bytes = MR.files.Compose.readText().encodeToByteArray()
+ return ByteReadChannel(bytes)
+}
+
+actual suspend fun svgImage(): ByteReadChannel {
+ val bytes = MR.files.Kotlin.readText().encodeToByteArray()
+ return ByteReadChannel(bytes)
+}
diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock
new file mode 100644
index 00000000..10befde4
--- /dev/null
+++ b/kotlin-js-store/yarn.lock
@@ -0,0 +1,3265 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.10.4":
+ version "7.21.4"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39"
+ integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==
+ dependencies:
+ "@babel/highlight" "^7.18.6"
+
+"@babel/helper-validator-identifier@^7.18.6":
+ version "7.19.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+ integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+
+"@babel/highlight@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+ integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.18.6"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
+"@colors/colors@1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
+ integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+
+"@discoveryjs/json-ext@^0.5.0":
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
+ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
+
+"@jridgewell/gen-mapping@^0.3.0":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+ 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"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.1":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+ integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+ integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.15"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
+ integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@leichtgewicht/ip-codec@^2.0.1":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
+ integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
+
+"@messageformat/core@3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.0.0.tgz#8785c283218a11ced7f1474f547824bbce583f21"
+ integrity sha512-mzMCyADGweNmnMwV/xjKxtXtkXJNVcK5ATOHx4Q0GVr2Z3++in1VfrB/y6jM43nleK27tFO0Y/9pds5vxWqDvQ==
+ dependencies:
+ "@messageformat/date-skeleton" "^1.0.0"
+ "@messageformat/number-skeleton" "^1.0.0"
+ "@messageformat/parser" "^5.0.0"
+ "@messageformat/runtime" "^3.0.0"
+ make-plural "^6.2.1"
+ safe-identifier "^0.4.1"
+
+"@messageformat/date-skeleton@^1.0.0":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz#980b8babe21a11433b6e1e8f6dc8c4cae4f5f56b"
+ integrity sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==
+
+"@messageformat/number-skeleton@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@messageformat/number-skeleton/-/number-skeleton-1.1.0.tgz#eb636738da8abbd35ccbeb84f7d84d63302aeb61"
+ integrity sha512-F0Io+GOSvFFxvp9Ze3L5kAoZ2NnOAT0Mr/jpGNd3fqo8A0t4NxNIAcCdggtl2B/gN2ErkIKSBVPrF7xcW1IGvA==
+
+"@messageformat/parser@^5.0.0":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.0.0.tgz#5737e69d7d4a469998b527710f1891174fc1b262"
+ integrity sha512-WiDKhi8F0zQaFU8cXgqq69eYFarCnTVxKcvhAONufKf0oUxbqLMW6JX6rV4Hqh+BEQWGyKKKHY4g1XA6bCLylA==
+ dependencies:
+ moo "^0.5.1"
+
+"@messageformat/runtime@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@messageformat/runtime/-/runtime-3.0.1.tgz#94d1f6c43265c28ef7aed98ecfcc0968c6c849ac"
+ integrity sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==
+ dependencies:
+ make-plural "^7.0.0"
+
+"@rollup/plugin-commonjs@^21.0.1":
+ version "21.1.0"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz#45576d7b47609af2db87f55a6d4b46e44fc3a553"
+ integrity sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==
+ dependencies:
+ "@rollup/pluginutils" "^3.1.0"
+ commondir "^1.0.1"
+ estree-walker "^2.0.1"
+ glob "^7.1.6"
+ is-reference "^1.2.1"
+ magic-string "^0.25.7"
+ resolve "^1.17.0"
+
+"@rollup/plugin-node-resolve@^13.1.3":
+ version "13.3.0"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c"
+ integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==
+ dependencies:
+ "@rollup/pluginutils" "^3.1.0"
+ "@types/resolve" "1.17.1"
+ deepmerge "^4.2.2"
+ is-builtin-module "^3.1.0"
+ is-module "^1.0.0"
+ resolve "^1.19.0"
+
+"@rollup/plugin-typescript@^8.3.0":
+ version "8.5.0"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515"
+ integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==
+ dependencies:
+ "@rollup/pluginutils" "^3.1.0"
+ resolve "^1.17.0"
+
+"@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
+ integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
+ dependencies:
+ "@types/estree" "0.0.39"
+ estree-walker "^1.0.1"
+ picomatch "^2.2.2"
+
+"@socket.io/component-emitter@~3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
+ integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
+
+"@types/body-parser@*":
+ version "1.19.2"
+ resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
+ integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/bonjour@^3.5.9":
+ version "3.5.10"
+ resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275"
+ integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/connect-history-api-fallback@^1.3.5":
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae"
+ integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==
+ dependencies:
+ "@types/express-serve-static-core" "*"
+ "@types/node" "*"
+
+"@types/connect@*":
+ version "3.4.35"
+ resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
+ integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/cookie@^0.4.1":
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
+ integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
+
+"@types/cors@^2.8.12":
+ version "2.8.12"
+ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080"
+ integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==
+
+"@types/eslint-scope@^3.7.3":
+ version "3.7.4"
+ resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
+ integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==
+ dependencies:
+ "@types/eslint" "*"
+ "@types/estree" "*"
+
+"@types/eslint@*":
+ version "8.4.6"
+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.6.tgz#7976f054c1bccfcf514bff0564c0c41df5c08207"
+ integrity sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==
+ dependencies:
+ "@types/estree" "*"
+ "@types/json-schema" "*"
+
+"@types/estree@*":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
+ integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
+
+"@types/estree@0.0.39":
+ version "0.0.39"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
+ integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
+
+"@types/estree@^0.0.51":
+ version "0.0.51"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
+ integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
+
+"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18":
+ version "4.17.30"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04"
+ integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+
+"@types/express@*", "@types/express@^4.17.13":
+ version "4.17.13"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
+ integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^4.17.18"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/http-proxy@^1.17.8":
+ version "1.17.9"
+ resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a"
+ integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+ version "7.0.11"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/mime@*":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
+ integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
+
+"@types/node@*", "@types/node@>=10.0.0":
+ version "18.7.17"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.17.tgz#52438111ea98f77475470fc62d79b9eb96bb2c92"
+ integrity sha512-0UyfUnt02zIuqp7yC8RYtDkp/vo8bFaQ13KkSEvUAohPOAlnVNbj5Fi3fgPSuwzakS+EvvnnZ4x9y7i6ASaSPQ==
+
+"@types/node@^12.12.14":
+ version "12.20.55"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
+ integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
+
+"@types/qs@*":
+ version "6.9.7"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
+ integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+
+"@types/range-parser@*":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
+ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
+
+"@types/resolve@1.17.1":
+ version "1.17.1"
+ resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
+ integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/retry@0.12.0":
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
+ integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
+
+"@types/serve-index@^1.9.1":
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278"
+ integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==
+ dependencies:
+ "@types/express" "*"
+
+"@types/serve-static@*", "@types/serve-static@^1.13.10":
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155"
+ integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==
+ dependencies:
+ "@types/mime" "*"
+ "@types/node" "*"
+
+"@types/sockjs@^0.3.33":
+ version "0.3.33"
+ resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f"
+ integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/ws@^8.5.1":
+ version "8.5.3"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
+ integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
+ dependencies:
+ "@types/node" "*"
+
+"@ungap/promise-all-settled@1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
+ integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
+
+"@webassemblyjs/ast@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
+ integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
+ dependencies:
+ "@webassemblyjs/helper-numbers" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+
+"@webassemblyjs/floating-point-hex-parser@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
+ integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
+
+"@webassemblyjs/helper-api-error@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
+ integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
+
+"@webassemblyjs/helper-buffer@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
+ integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
+
+"@webassemblyjs/helper-numbers@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
+ 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"
+
+"@webassemblyjs/helper-wasm-bytecode@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
+ integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
+
+"@webassemblyjs/helper-wasm-section@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
+ 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"
+
+"@webassemblyjs/ieee754@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
+ integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
+ integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
+ dependencies:
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
+ integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
+
+"@webassemblyjs/wasm-edit@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
+ 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"
+
+"@webassemblyjs/wasm-gen@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
+ 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"
+
+"@webassemblyjs/wasm-opt@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
+ 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"
+
+"@webassemblyjs/wasm-parser@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
+ 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"
+
+"@webassemblyjs/wast-printer@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
+ integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@xtuc/long" "4.2.2"
+
+"@webpack-cli/configtest@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
+ integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
+
+"@webpack-cli/info@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
+ integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
+ dependencies:
+ envinfo "^7.7.3"
+
+"@webpack-cli/serve@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
+ integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
+
+"@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.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
+ integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
+
+abort-controller@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
+ integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
+ dependencies:
+ event-target-shim "^5.0.0"
+
+accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn-import-assertions@^1.7.6:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
+ integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
+
+acorn@^8.5.0:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
+ integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+
+acorn@^8.7.1:
+ version "8.8.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+ integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+
+ajv-formats@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
+ integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
+ dependencies:
+ ajv "^8.0.0"
+
+ajv-keywords@^3.5.2:
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
+ajv-keywords@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
+ integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+
+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.0, ajv@^8.8.0:
+ version "8.11.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
+ integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+ uri-js "^4.2.2"
+
+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-html-community@^0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
+ integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+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@~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"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-flatten@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+ integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
+
+array-flatten@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
+ integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
+
+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==
+
+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==
+
+base64id@2.0.0, base64id@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
+ integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
+
+batch@0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
+ integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==
+
+bcp-47@1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/bcp-47/-/bcp-47-1.0.8.tgz#bf63ae4269faabe7c100deac0811121a48b6a561"
+ integrity sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==
+ dependencies:
+ is-alphabetical "^1.0.0"
+ is-alphanumerical "^1.0.0"
+ is-decimal "^1.0.0"
+
+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==
+
+body-parser@1.20.0, body-parser@^1.19.0:
+ version "1.20.0"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
+ integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==
+ 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.10.3"
+ raw-body "2.5.1"
+ type-is "~1.6.18"
+ unpipe "1.0.0"
+
+bonjour-service@^1.0.11:
+ version "1.0.14"
+ resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7"
+ integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==
+ dependencies:
+ array-flatten "^2.1.2"
+ dns-equal "^1.0.0"
+ fast-deep-equal "^3.1.3"
+ multicast-dns "^7.2.5"
+
+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"
+
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.2, 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"
+
+browser-stdout@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+ integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
+
+browserslist@^4.14.5:
+ version "4.21.3"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
+ integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==
+ dependencies:
+ caniuse-lite "^1.0.30001370"
+ electron-to-chromium "^1.4.202"
+ node-releases "^2.0.6"
+ update-browserslist-db "^1.0.5"
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+builtin-modules@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
+ integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
+
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
+
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+call-bind@^1.0.0:
+ 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"
+
+camelcase@^6.0.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+caniuse-lite@^1.0.30001370:
+ version "1.0.30001397"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz#010d9d56e3b8abcd8df261d0a94b22426271a15f"
+ integrity sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==
+
+chalk@^2.0.0:
+ 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.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chokidar@3.5.3, chokidar@^3.5.1, chokidar@^3.5.3:
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+ integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+ 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"
+
+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==
+
+cliui@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^7.0.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"
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+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==
+
+colorette@^2.0.10, colorette@^2.0.14:
+ version "2.0.19"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
+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@^7.0.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 sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
+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@^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 sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+connect-history-api-fallback@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
+ integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
+
+connect@^3.7.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
+ integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==
+ dependencies:
+ debug "2.6.9"
+ finalhandler "1.1.2"
+ parseurl "~1.3.3"
+ utils-merge "1.0.1"
+
+content-disposition@0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+ dependencies:
+ safe-buffer "5.2.1"
+
+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==
+
+cookie-signature@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+ integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
+
+cookie@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
+ integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+
+cookie@~0.4.1:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
+ integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
+
+core-util-is@~1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+ integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+cors@~2.8.5:
+ version "2.8.5"
+ resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
+ integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
+ dependencies:
+ object-assign "^4"
+ vary "^1"
+
+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"
+
+css-loader@6.7.3:
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.3.tgz#1e8799f3ccc5874fdd55461af51137fcc5befbcd"
+ integrity sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==
+ dependencies:
+ icss-utils "^5.1.0"
+ postcss "^8.4.19"
+ 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.8"
+
+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==
+
+custom-event@~1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
+ integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==
+
+date-format@^4.0.13:
+ version "4.0.13"
+ resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.13.tgz#87c3aab3a4f6f37582c5f5f63692d2956fa67890"
+ integrity sha512-bnYCwf8Emc3pTD8pXnre+wfnjGtfi5ncMDKy7+cWZXbmRAsdWkOQHrfC1yz/KiwP5thDp2kCHWYWKBX4HP1hoQ==
+
+debug@2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@4.3.4, debug@^4.1.0, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+decamelize@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
+ integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
+
+decode-uri-component@^0.2.0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
+ integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
+
+deepmerge@^4.2.2:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+ integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
+default-gateway@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71"
+ integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==
+ dependencies:
+ execa "^5.0.0"
+
+define-lazy-prop@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
+ integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+
+depd@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+ integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
+
+destroy@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+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==
+
+di@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
+ integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==
+
+diff@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
+ integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
+
+dns-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
+ integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==
+
+dns-packet@^5.2.2:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b"
+ integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==
+ dependencies:
+ "@leichtgewicht/ip-codec" "^2.0.1"
+
+dom-serialize@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
+ integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==
+ dependencies:
+ custom-event "~1.0.0"
+ ent "~2.2.0"
+ extend "^3.0.0"
+ void-elements "^2.0.0"
+
+dukat@0.5.8-rc.4:
+ version "0.5.8-rc.4"
+ resolved "https://registry.yarnpkg.com/dukat/-/dukat-0.5.8-rc.4.tgz#90384dcb50b14c26f0e99dae92b2dea44f5fce21"
+ integrity sha512-ZnMt6DGBjlVgK2uQamXfd7uP/AxH7RqI0BL9GLrrJb2gKdDxvJChWy+M9AQEaL+7/6TmxzJxFOsRiInY9oGWTA==
+ dependencies:
+ google-protobuf "3.12.2"
+ typescript "3.9.5"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+electron-to-chromium@^1.4.202:
+ version "1.4.248"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.248.tgz#dd2dab68277e91e8452536ee9265f484066f94ad"
+ integrity sha512-qShjzEYpa57NnhbW2K+g+Fl+eNoDvQ7I+2MRwWnU6Z6F0HhXekzsECCLv+y2OJUsRodjqoSfwHkIX42VUFtUzg==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
+engine.io-parser@~5.0.3:
+ version "5.0.4"
+ resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
+ integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
+
+engine.io@~6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0"
+ integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==
+ 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"
+
+enhanced-resolve@^5.10.0:
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275"
+ integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
+ent@~2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
+ integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==
+
+envinfo@^7.7.3:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
+ integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
+
+es-module-lexer@^0.9.0:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
+ integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
+
+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 sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+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==
+
+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 sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+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"
+
+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.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+estree-walker@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
+ integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
+
+estree-walker@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+ integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+
+event-target-shim@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
+ integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
+
+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.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+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"
+
+express@^4.17.3:
+ version "4.18.1"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf"
+ integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
+ dependencies:
+ accepts "~1.3.8"
+ array-flatten "1.1.1"
+ body-parser "1.20.0"
+ 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.10.3"
+ 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"
+
+extend@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+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-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==
+
+fastest-levenshtein@^1.0.12:
+ version "1.0.16"
+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
+ integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
+
+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"
+
+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"
+
+finalhandler@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
+ integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
+ 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"
+
+find-up@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.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@^5.0.2:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
+ integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
+
+flatted@^3.2.6:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
+ integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
+
+follow-redirects@^1.0.0:
+ version "1.15.1"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
+ integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
+
+format-util@1.0.5, format-util@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271"
+ integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==
+
+forwarded@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+ integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+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-monkey@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
+ integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+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==
+
+get-caller-file@^2.0.5:
+ 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:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
+ integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
+ dependencies:
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.3"
+
+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==
+
+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-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
+glob@7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+ 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"
+
+glob@^7.1.3, glob@^7.1.6, glob@^7.1.7:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ 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"
+
+google-protobuf@3.12.2:
+ version "3.12.2"
+ resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.2.tgz#50ce9f9b6281235724eb243d6a83e969a2176e53"
+ integrity sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA==
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+
+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-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+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.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+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"
+
+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==
+
+hpack.js@^2.1.6:
+ version "2.1.6"
+ resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
+ integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==
+ dependencies:
+ inherits "^2.0.1"
+ obuf "^1.0.0"
+ readable-stream "^2.0.1"
+ wbuf "^1.1.0"
+
+html-entities@^2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
+ integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==
+
+http-deceiver@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+ integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==
+
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
+http-errors@~1.6.2:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+ integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
+ dependencies:
+ depd "~1.1.2"
+ inherits "2.0.3"
+ setprototypeof "1.1.0"
+ statuses ">= 1.4.0 < 2"
+
+http-parser-js@>=0.5.1:
+ version "0.5.8"
+ resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3"
+ integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==
+
+http-proxy-middleware@^2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
+ integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
+ 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"
+
+http-proxy@^1.18.1:
+ version "1.18.1"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
+ integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
+ dependencies:
+ eventemitter3 "^4.0.0"
+ follow-redirects "^1.0.0"
+ requires-port "^1.0.0"
+
+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==
+
+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.3:
+ 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==
+
+import-local@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
+ integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, 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.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+ integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
+
+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==
+
+ipaddr.js@1.9.1:
+ 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==
+
+ipaddr.js@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
+ integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
+
+is-alphabetical@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d"
+ integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==
+
+is-alphanumerical@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf"
+ integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==
+ dependencies:
+ is-alphabetical "^1.0.0"
+ is-decimal "^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-builtin-module@^3.1.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169"
+ integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==
+ dependencies:
+ builtin-modules "^3.3.0"
+
+is-core-module@^2.11.0:
+ version "2.12.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4"
+ integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==
+ dependencies:
+ has "^1.0.3"
+
+is-core-module@^2.9.0:
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
+ integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
+ dependencies:
+ has "^1.0.3"
+
+is-decimal@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5"
+ integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==
+
+is-docker@^2.0.0, is-docker@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+ integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+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@^4.0.1, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
+ integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
+
+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-plain-obj@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+ integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+
+is-plain-obj@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
+ integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
+
+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-reference@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
+ integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
+ dependencies:
+ "@types/estree" "*"
+
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
+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-wsl@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+ integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+ dependencies:
+ is-docker "^2.0.0"
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
+isbinaryfile@^4.0.8:
+ version "4.0.10"
+ resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3"
+ integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
+
+jest-worker@^26.2.1:
+ version "26.6.2"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
+ integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^7.0.0"
+
+jest-worker@^27.4.5:
+ version "27.5.1"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
+ integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.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@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+json-parse-even-better-errors@^2.3.1:
+ 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==
+
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+karma-chrome-launcher@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz#baca9cc071b1562a1db241827257bfe5cab597ea"
+ integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==
+ dependencies:
+ which "^1.2.1"
+
+karma-mocha@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d"
+ integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==
+ dependencies:
+ minimist "^1.2.3"
+
+karma-sourcemap-loader@0.3.8:
+ version "0.3.8"
+ resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz#d4bae72fb7a8397328a62b75013d2df937bdcf9c"
+ integrity sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g==
+ dependencies:
+ graceful-fs "^4.1.2"
+
+karma-webpack@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840"
+ integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA==
+ dependencies:
+ glob "^7.1.3"
+ minimatch "^3.0.4"
+ webpack-merge "^4.1.5"
+
+karma@6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.0.tgz#82652dfecdd853ec227b74ed718a997028a99508"
+ integrity sha512-s8m7z0IF5g/bS5ONT7wsOavhW4i4aFkzD4u4wgzAQWT4HGUeWI3i21cK2Yz6jndMAeHETp5XuNsRoyGJZXVd4w==
+ 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"
+
+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==
+
+loader-runner@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
+ integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+
+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"
+
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
+lodash@^4.17.15, lodash@^4.17.21:
+ 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"
+
+log4js@^6.4.1:
+ version "6.6.1"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.6.1.tgz#48f23de8a87d2f5ffd3d913f24ca9ce77895272f"
+ integrity sha512-J8VYFH2UQq/xucdNu71io4Fo+purYYudyErgBbswWKO0MC6QVOERRomt5su/z6d3RJSmLyTGmXl3Q/XjKCf+/A==
+ dependencies:
+ date-format "^4.0.13"
+ debug "^4.3.4"
+ flatted "^3.2.6"
+ rfdc "^1.3.0"
+ streamroller "^3.1.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"
+
+magic-string@^0.25.7:
+ version "0.25.9"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
+ integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
+ dependencies:
+ sourcemap-codec "^1.4.8"
+
+make-plural@^6.2.1:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-6.2.2.tgz#beb5fd751355e72660eeb2218bb98eec92853c6c"
+ integrity sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==
+
+make-plural@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.1.0.tgz#8a0381ff8c9be4f074e0acdc42ec97639c2344f9"
+ integrity sha512-PKkwVlAxYVo98NrbclaQIT4F5Oy+X58PZM5r2IwUSCe3syya6PXkIRCn2XCdz7p58Scgpp50PBeHmepXVDG3hg==
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+ integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
+memfs@^3.4.3:
+ version "3.4.7"
+ resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.7.tgz#e5252ad2242a724f938cb937e3c4f7ceb1f70e5a"
+ integrity sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==
+ dependencies:
+ fs-monkey "^1.0.3"
+
+merge-descriptors@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+ integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+
+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==
+
+methods@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+ integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
+
+micromatch@^4.0.2:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.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.5.2:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
+ integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
+
+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@2.6.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e"
+ integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==
+ dependencies:
+ schema-utils "^4.0.0"
+
+minimalistic-assert@^1.0.0:
+ 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==
+
+minimatch@5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
+ integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
+ dependencies:
+ brace-expansion "^2.0.1"
+
+minimatch@^3.0.4, minimatch@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@^1.2.3, minimist@^1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+ integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+
+mkdirp@^0.5.5:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
+ integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
+ dependencies:
+ minimist "^1.2.6"
+
+mocha@10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9"
+ integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==
+ dependencies:
+ "@ungap/promise-all-settled" "1.1.2"
+ ansi-colors "4.1.1"
+ browser-stdout "1.3.1"
+ chokidar "3.5.3"
+ debug "4.3.4"
+ diff "5.0.0"
+ escape-string-regexp "4.0.0"
+ find-up "5.0.0"
+ glob "7.2.0"
+ he "1.2.0"
+ js-yaml "4.1.0"
+ log-symbols "4.1.0"
+ minimatch "5.0.1"
+ ms "2.1.3"
+ nanoid "3.3.3"
+ serialize-javascript "6.0.0"
+ strip-json-comments "3.1.1"
+ supports-color "8.1.1"
+ workerpool "6.2.1"
+ yargs "16.2.0"
+ yargs-parser "20.2.4"
+ yargs-unparser "2.0.0"
+
+moo@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
+ integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
+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.3:
+ 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@^7.2.5:
+ version "7.2.5"
+ resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced"
+ integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==
+ dependencies:
+ dns-packet "^5.2.2"
+ thunky "^1.0.2"
+
+nanoid@3.3.3:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
+ integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
+
+nanoid@^3.3.6:
+ version "3.3.6"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
+ integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+neo-async@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+node-fetch@2.6.7:
+ version "2.6.7"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
+ integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
+ dependencies:
+ whatwg-url "^5.0.0"
+
+node-forge@^1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
+ integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
+
+node-releases@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
+ integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
+
+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==
+
+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"
+
+object-assign@^4:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-inspect@^1.9.0:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
+ integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
+
+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.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
+on-finished@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==
+ 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:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+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"
+
+open@^8.0.9:
+ version "8.4.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
+ integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
+ dependencies:
+ define-lazy-prop "^2.0.0"
+ is-docker "^2.1.1"
+ is-wsl "^2.2.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-limit@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.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-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
+p-retry@^4.5.0:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
+ integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
+ dependencies:
+ "@types/retry" "0.12.0"
+ retry "^0.13.1"
+
+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==
+
+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==
+
+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 sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+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.7:
+ 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 sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+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"
+
+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-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
+ version "6.0.12"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz#2efae5ffab3c8bfb2b7fbf0c426e3bca616c4abb"
+ integrity sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==
+ dependencies:
+ cssesc "^3.0.0"
+ util-deprecate "^1.0.2"
+
+postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@^8.4.19:
+ version "8.4.23"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
+ integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
+ dependencies:
+ nanoid "^3.3.6"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+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==
+
+proxy-addr@~2.0.7:
+ 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"
+
+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==
+
+qjobs@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
+ integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==
+
+qs@6.10.3:
+ version "6.10.3"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
+ integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
+ dependencies:
+ side-channel "^1.0.4"
+
+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"
+
+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.5.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
+ integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ unpipe "1.0.0"
+
+readable-stream@^2.0.1:
+ 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:
+ 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@~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.1"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
+ integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
+ dependencies:
+ resolve "^1.9.0"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+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==
+
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+
+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@^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@^1.17.0, resolve@^1.19.0:
+ version "1.22.2"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f"
+ integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==
+ dependencies:
+ is-core-module "^2.11.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+resolve@^1.9.0:
+ version "1.22.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+ integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
+ dependencies:
+ is-core-module "^2.9.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+retry@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
+ integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
+
+rfdc@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
+ integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
+
+rimraf@^3.0.0, rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+rollup-plugin-sourcemaps@^0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed"
+ integrity sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==
+ dependencies:
+ "@rollup/pluginutils" "^3.0.9"
+ source-map-resolve "^0.6.0"
+
+rollup-plugin-terser@^7.0.2:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d"
+ integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ jest-worker "^26.2.1"
+ serialize-javascript "^4.0.0"
+ terser "^5.0.0"
+
+rollup@^2.68.0:
+ version "2.79.1"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
+ integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+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.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.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-identifier@^0.4.1:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb"
+ integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==
+
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+schema-utils@^3.1.0, schema-utils@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
+ integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
+ dependencies:
+ "@types/json-schema" "^7.0.8"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
+schema-utils@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
+ integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==
+ dependencies:
+ "@types/json-schema" "^7.0.9"
+ ajv "^8.8.0"
+ ajv-formats "^2.1.1"
+ ajv-keywords "^5.0.0"
+
+select-hose@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+ integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
+
+selfsigned@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61"
+ integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==
+ dependencies:
+ node-forge "^1"
+
+semver@^7.3.8:
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0"
+ integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==
+ dependencies:
+ lru-cache "^6.0.0"
+
+send@0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
+ integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+ 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"
+
+serialize-javascript@6.0.0, serialize-javascript@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+ integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+ dependencies:
+ randombytes "^2.1.0"
+
+serialize-javascript@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
+ integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
+ dependencies:
+ randombytes "^2.1.0"
+
+serve-index@^1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
+ integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==
+ 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.15.0:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
+ integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+ dependencies:
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.18.0"
+
+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.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+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@^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@^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==
+
+side-channel@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+ integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
+ dependencies:
+ call-bind "^1.0.0"
+ get-intrinsic "^1.0.2"
+ object-inspect "^1.9.0"
+
+signal-exit@^3.0.3:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+socket.io-adapter@~2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6"
+ integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==
+
+socket.io-parser@~4.2.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
+ integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.1"
+
+socket.io@^4.4.1:
+ version "4.5.2"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.2.tgz#1eb25fd380ab3d63470aa8279f8e48d922d443ac"
+ integrity sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==
+ dependencies:
+ accepts "~1.3.4"
+ base64id "~2.0.0"
+ debug "~4.3.2"
+ engine.io "~6.2.0"
+ socket.io-adapter "~2.4.0"
+ socket.io-parser "~4.2.0"
+
+sockjs@^0.3.24:
+ version "0.3.24"
+ resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"
+ integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==
+ dependencies:
+ faye-websocket "^0.11.3"
+ uuid "^8.3.2"
+ websocket-driver "^0.7.4"
+
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map-loader@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.0.tgz#bdc6b118bc6c87ee4d8d851f2d4efcc5abdb2ef5"
+ integrity sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==
+ dependencies:
+ abab "^2.0.6"
+ iconv-lite "^0.6.3"
+ source-map-js "^1.0.2"
+
+source-map-resolve@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
+ integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
+ dependencies:
+ atob "^2.1.2"
+ decode-uri-component "^0.2.0"
+
+source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+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==
+
+sourcemap-codec@^1.4.8:
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+ integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
+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"
+
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+"statuses@>= 1.4.0 < 2", statuses@~1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
+
+streamroller@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.2.tgz#abd444560768b340f696307cf84d3f46e86c0e63"
+ integrity sha512-wZswqzbgGGsXYIrBYhOE0yP+nQ6XRk7xDcYwuQAGTYXdyAUmvgVFE0YU1g5pvQT0m7GBaQfYcSnlHbapuK0H0A==
+ dependencies:
+ date-format "^4.0.13"
+ debug "^4.3.4"
+ fs-extra "^8.1.0"
+
+string-width@^4.1.0, string-width@^4.2.0:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+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.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@3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
+ integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
+
+supports-color@8.1.1, supports-color@^8.0.0:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^7.0.0, supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+tapable@^2.1.1, tapable@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
+ integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
+
+terser-webpack-plugin@^5.1.3:
+ version "5.3.6"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c"
+ 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"
+
+terser@^5.0.0:
+ version "5.17.1"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69"
+ integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.2"
+ acorn "^8.5.0"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+terser@^5.14.1:
+ version "5.15.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425"
+ integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.2"
+ acorn "^8.5.0"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+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==
+
+tmp@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
+ integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
+ dependencies:
+ rimraf "^3.0.0"
+
+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"
+
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
+tslib@^2.3.1:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
+ integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
+
+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"
+
+typescript@3.9.5:
+ version "3.9.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
+ integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
+
+typescript@4.7.4:
+ version "4.7.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
+ integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
+
+typescript@^3.7.2:
+ version "3.9.10"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
+ integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
+
+ua-parser-js@^0.7.30:
+ version "0.7.31"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
+ integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
+
+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 sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+update-browserslist-db@^1.0.5:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz#2f0b711327668eee01bbecddcf4a7c7954a7f8e2"
+ integrity sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA==
+ dependencies:
+ escalade "^3.1.1"
+ picocolors "^1.0.0"
+
+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"
+
+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 sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
+uuid@^8.3.2:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+vary@^1, vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+void-elements@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
+ integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==
+
+watchpack@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
+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"
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+webpack-cli@4.10.0:
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
+ 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"
+
+webpack-dev-middleware@^5.3.1:
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f"
+ integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==
+ dependencies:
+ colorette "^2.0.10"
+ memfs "^3.4.3"
+ mime-types "^2.1.31"
+ range-parser "^1.2.1"
+ schema-utils "^4.0.0"
+
+webpack-dev-server@4.9.3:
+ version "4.9.3"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz#2360a5d6d532acb5410a668417ad549ee3b8a3c9"
+ integrity sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==
+ 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"
+
+webpack-merge@^4.1.5:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d"
+ integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==
+ dependencies:
+ lodash "^4.17.15"
+
+webpack-merge@^5.7.3:
+ 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@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
+ integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+
+webpack@5.74.0:
+ version "5.74.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980"
+ integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==
+ 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"
+
+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-url@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
+
+which@^1.2.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+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==
+
+workerpool@6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
+ integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
+
+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 sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+ws@8.5.0:
+ version "8.5.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
+ integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
+
+ws@^8.4.2:
+ version "8.8.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+ integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
+
+ws@~8.2.3:
+ version "8.2.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
+ integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yargs-parser@20.2.4:
+ version "20.2.4"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
+ integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
+
+yargs-parser@^20.2.2:
+ version "20.2.9"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
+ integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+
+yargs-unparser@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
+ integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
+ dependencies:
+ camelcase "^6.0.0"
+ decamelize "^4.0.0"
+ flat "^5.0.2"
+ is-plain-obj "^2.1.0"
+
+yargs@16.2.0, yargs@^16.1.1:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+ integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+ 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"
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==