diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 077c3492..4e24fe4a 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -8,8 +8,9 @@ repositories { } dependencies { - val kotlinVersion = "2.3.20" + val kotlinVersion = "2.3.0" + implementation("org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin:${kotlinVersion}") implementation("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:$kotlinVersion") implementation("org.jetbrains.kotlin.plugin.allopen:org.jetbrains.kotlin.plugin.allopen.gradle.plugin:$kotlinVersion") implementation("org.jetbrains.kotlin.plugin.noarg:org.jetbrains.kotlin.plugin.noarg.gradle.plugin:$kotlinVersion") diff --git a/buildSrc/src/main/kotlin/library-module-conventions.gradle.kts b/buildSrc/src/main/kotlin/library-module-conventions.gradle.kts index ebbbbd29..60477e16 100644 --- a/buildSrc/src/main/kotlin/library-module-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/library-module-conventions.gradle.kts @@ -24,8 +24,9 @@ kotlin { "com.github.avrokotlin.avro4k.ExperimentalAvro4kApi", ) freeCompilerArgs.add("-Xcontext-parameters") - jvmToolchain(11) } + + jvmToolchain(11) } tasks.withType().configureEach { diff --git a/buildSrc/src/main/kotlin/library-multiplatform-module-conventions.gradle.kts b/buildSrc/src/main/kotlin/library-multiplatform-module-conventions.gradle.kts new file mode 100644 index 00000000..1dc2a810 --- /dev/null +++ b/buildSrc/src/main/kotlin/library-multiplatform-module-conventions.gradle.kts @@ -0,0 +1,49 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.dokka") + id("org.jetbrains.kotlinx.binary-compatibility-validator") + id("com.diffplug.spotless") +} + +apiValidation { + nonPublicMarkers += "com.github.avrokotlin.avro4k.InternalAvro4kApi" +} + +java { + withSourcesJar() +} + +kotlin { + explicitApi() + + compilerOptions { + optIn = listOf( + "com.github.avrokotlin.avro4k.InternalAvro4kApi", + "com.github.avrokotlin.avro4k.ExperimentalAvro4kApi", + ) + freeCompilerArgs = listOf( + "-Xcontext-parameters", + "-Xexpect-actual-classes", + ) + } + + jvmToolchain(11) +} + +tasks.withType().configureEach { + useJUnitPlatform() +} + +spotless { + val ktlintVersion = extensions.getByType().named("libs").findVersion("ktlint").get().toString() + kotlin { + ktlint(ktlintVersion).setEditorConfigPath(rootProject.file(".editorconfig")) + } + kotlinGradle { + ktlint(ktlintVersion).setEditorConfigPath(rootProject.file(".editorconfig")) + } +} + +repositories { + mavenCentral() +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/library-publish-conventions.gradle.kts b/buildSrc/src/main/kotlin/library-publish-conventions.gradle.kts index 6a010488..f3726fbc 100644 --- a/buildSrc/src/main/kotlin/library-publish-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/library-publish-conventions.gradle.kts @@ -12,40 +12,43 @@ mavenPublishing { } } -mavenPublishing { - coordinates( - artifactId = "avro4k-${project.name}", - ) - pom { - val projectUrl = "https://github.com/avro-kotlin/avro4k" - name = project.name - description = project.description?.ifEmpty { null } ?: error("Missing ${project.name} project description") - url = projectUrl - - scm { - connection = "scm:git:$projectUrl" - developerConnection = "scm:git:$projectUrl" +// Needed to wrap in afterEvaluate as the project.description is not evaluated early enough in kmp I don't know why +afterEvaluate { + mavenPublishing { + coordinates( + artifactId = "avro4k-${project.name}", + ) + pom { + val projectUrl = "https://github.com/avro-kotlin/avro4k" + name = project.name + description = project.description?.ifEmpty { null } ?: error("Missing ${project.name} project description") url = projectUrl - } - licenses { - license { - name = "Apache-2.0" - url = "https://opensource.org/licenses/Apache-2.0" + scm { + connection = "scm:git:$projectUrl" + developerConnection = "scm:git:$projectUrl" + url = projectUrl } - } - developers { - developer { - id = "thake" - name = "Thorsten Hake" - email = "mail@thorsten-hake.com" + licenses { + license { + name = "Apache-2.0" + url = "https://opensource.org/licenses/Apache-2.0" + } } - developer { - id = "chuckame" - name = "Antoine Michaud" - email = "contact@antoine-michaud.fr" + + developers { + developer { + id = "thake" + name = "Thorsten Hake" + email = "mail@thorsten-hake.com" + } + developer { + id = "chuckame" + name = "Antoine Michaud" + email = "contact@antoine-michaud.fr" + } } } } -} \ No newline at end of file +} diff --git a/confluent-kafka-serializer/src/main/kotlin/com/github/avrokotlin/avro4k/kafka/confluent/Serializers.kt b/confluent-kafka-serializer/src/main/kotlin/com/github/avrokotlin/avro4k/kafka/confluent/Serializers.kt index 49898a43..140d9e25 100644 --- a/confluent-kafka-serializer/src/main/kotlin/com/github/avrokotlin/avro4k/kafka/confluent/Serializers.kt +++ b/confluent-kafka-serializer/src/main/kotlin/com/github/avrokotlin/avro4k/kafka/confluent/Serializers.kt @@ -5,8 +5,7 @@ import com.github.avrokotlin.avro4k.AvroAlias import com.github.avrokotlin.avro4k.AvroDecoder import com.github.avrokotlin.avro4k.AvroEncoder import com.github.avrokotlin.avro4k.MissingFieldsEncodingException -import com.github.avrokotlin.avro4k.internal.Cache -import com.github.avrokotlin.avro4k.internal.WeakKeyCache +import com.github.avrokotlin.avro4k.internal.WeakIdentityKeyCache import com.github.avrokotlin.avro4k.internal.aliases import com.github.avrokotlin.avro4k.internal.isNamedSchema import com.github.avrokotlin.avro4k.namedSchemaNotFoundInUnionError @@ -203,7 +202,7 @@ internal object GenericFixedKSerializer : AvroSerializer(GenericFi internal class GenericRecordKSerializer( private val anySerializer: AnySerializer, ) : AvroSerializer(IndexedRecord::class.qualifiedName!!) { - private val cache: Cache = WeakKeyCache() + private val cache = WeakIdentityKeyCache() override fun serializeAvro(encoder: AvroEncoder, value: IndexedRecord) { with(encoder) { diff --git a/core/api/core.api b/core/api/core.api index 09f1c6fa..27f4e616 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -228,6 +228,324 @@ public final synthetic class com/github/avrokotlin/avro4k/AvroProp$Impl : com/gi public final synthetic fun value ()Ljava/lang/String; } +public abstract class com/github/avrokotlin/avro4k/AvroSchema { + public static final field Companion Lcom/github/avrokotlin/avro4k/AvroSchema$Companion; + public fun getFullName ()Ljava/lang/String; + public abstract fun getSimpleName ()Ljava/lang/String; + public abstract fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$ArraySchema : com/github/avrokotlin/avro4k/ResolvedSchema { + public fun (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;)V + public synthetic fun (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/github/avrokotlin/avro4k/AvroSchema; + public final fun component2 ()Ljava/util/Map; + public final fun copy (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$ArraySchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$ArraySchema;Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$ArraySchema; + public fun equals (Ljava/lang/Object;)Z + public final fun getElementSchema ()Lcom/github/avrokotlin/avro4k/AvroSchema; + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$BooleanSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$BooleanSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$BooleanSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$BooleanSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$BytesSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$BytesSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$BytesSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$BytesSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$Companion { +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$DoubleSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$DoubleSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$DoubleSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$DoubleSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$EnumSchema : com/github/avrokotlin/avro4k/AvroSchema$NamedSchema { + public fun (Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;)V + public synthetic fun (Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/github/avrokotlin/avro4k/Name; + public final fun component2 ()Ljava/util/List; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/util/Set; + public final fun component6 ()Ljava/util/Map; + public final fun copy (Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$EnumSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$EnumSchema;Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$EnumSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getAliases ()Ljava/util/Set; + public final fun getDefaultSymbol ()Ljava/lang/String; + public fun getDoc ()Ljava/lang/String; + public fun getName ()Lcom/github/avrokotlin/avro4k/Name; + public fun getProps ()Ljava/util/Map; + public final fun getSymbols ()Ljava/util/List; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$FixedSchema : com/github/avrokotlin/avro4k/AvroSchema$NamedSchema { + public synthetic fun (Lcom/github/avrokotlin/avro4k/Name;ILjava/lang/String;Ljava/util/Set;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lcom/github/avrokotlin/avro4k/Name;ILjava/lang/String;Ljava/util/Set;Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/github/avrokotlin/avro4k/Name; + public final fun component2-pVg5ArA ()I + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/util/Set; + public final fun component5 ()Ljava/util/Map; + public final fun copy-roUYKiI (Lcom/github/avrokotlin/avro4k/Name;ILjava/lang/String;Ljava/util/Set;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$FixedSchema; + public static synthetic fun copy-roUYKiI$default (Lcom/github/avrokotlin/avro4k/AvroSchema$FixedSchema;Lcom/github/avrokotlin/avro4k/Name;ILjava/lang/String;Ljava/util/Set;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$FixedSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getAliases ()Ljava/util/Set; + public fun getDoc ()Ljava/lang/String; + public fun getName ()Lcom/github/avrokotlin/avro4k/Name; + public fun getProps ()Ljava/util/Map; + public final fun getSize-pVg5ArA ()I + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$FloatSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$FloatSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$FloatSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$FloatSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$IntSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$IntSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$IntSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$IntSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$LongSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$LongSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$LongSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$LongSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$MapSchema : com/github/avrokotlin/avro4k/ResolvedSchema { + public fun (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;)V + public synthetic fun (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/github/avrokotlin/avro4k/AvroSchema; + public final fun component2 ()Ljava/util/Map; + public final fun copy (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$MapSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$MapSchema;Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$MapSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public final fun getValueSchema ()Lcom/github/avrokotlin/avro4k/AvroSchema; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class com/github/avrokotlin/avro4k/AvroSchema$NamedSchema : com/github/avrokotlin/avro4k/ResolvedSchema, com/github/avrokotlin/avro4k/WithDoc { + public abstract fun getAliases ()Ljava/util/Set; + public fun getFullName ()Ljava/lang/String; + public abstract fun getName ()Lcom/github/avrokotlin/avro4k/Name; + public fun getSimpleName ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$NullSchema : com/github/avrokotlin/avro4k/ResolvedSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$NullSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$NullSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$NullSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema : com/github/avrokotlin/avro4k/ResolvedSchema { +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$RecordSchema : com/github/avrokotlin/avro4k/AvroSchema$NamedSchema { + public fun (Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;)V + public synthetic fun (Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/github/avrokotlin/avro4k/Name; + public final fun component2 ()Ljava/util/List; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/util/Set; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema;Lcom/github/avrokotlin/avro4k/Name;Ljava/util/List;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getAliases ()Ljava/util/Set; + public fun getDoc ()Ljava/lang/String; + public final fun getFieldByName (Ljava/lang/String;)Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema$Field; + public final fun getFields ()Ljava/util/List; + public fun getName ()Lcom/github/avrokotlin/avro4k/Name; + public fun getProps ()Ljava/util/Map; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$RecordSchema$Field : com/github/avrokotlin/avro4k/WithDoc, com/github/avrokotlin/avro4k/WithProps { + public fun (Ljava/lang/String;Lcom/github/avrokotlin/avro4k/AvroSchema;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;)V + public synthetic fun (Ljava/lang/String;Lcom/github/avrokotlin/avro4k/AvroSchema;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lcom/github/avrokotlin/avro4k/AvroSchema; + public final fun component3 ()Lkotlinx/serialization/json/JsonElement; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/util/Set; + public final fun component6 ()Ljava/util/Map; + public final fun copy (Ljava/lang/String;Lcom/github/avrokotlin/avro4k/AvroSchema;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema$Field; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema$Field;Ljava/lang/String;Lcom/github/avrokotlin/avro4k/AvroSchema;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/util/Set;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$RecordSchema$Field; + public fun equals (Ljava/lang/Object;)Z + public final fun getAliases ()Ljava/util/Set; + public final fun getDefaultValue ()Lkotlinx/serialization/json/JsonElement; + public fun getDoc ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public fun getProps ()Ljava/util/Map; + public final fun getSchema ()Lcom/github/avrokotlin/avro4k/AvroSchema; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$StringSchema : com/github/avrokotlin/avro4k/AvroSchema$PrimitiveSchema { + public fun ()V + public fun (Ljava/util/Map;)V + public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema$StringSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$StringSchema;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$StringSchema; + public fun equals (Ljava/lang/Object;)Z + public fun getProps ()Ljava/util/Map; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$Type : java/lang/Enum { + public static final field ARRAY Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field BOOLEAN Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field BYTES Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field DOUBLE Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field ENUM Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field FIXED Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field FLOAT Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field INT Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field LONG Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field MAP Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field NULL Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field RECORD Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field STRING Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static final field UNION Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public static fun values ()[Lcom/github/avrokotlin/avro4k/AvroSchema$Type; +} + +public final class com/github/avrokotlin/avro4k/AvroSchema$UnionSchema : com/github/avrokotlin/avro4k/AvroSchema { + public fun (Ljava/util/List;)V + public fun ([Lcom/github/avrokotlin/avro4k/ResolvedSchema;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/github/avrokotlin/avro4k/AvroSchema$UnionSchema; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroSchema$UnionSchema;Ljava/util/List;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema$UnionSchema; + public fun equals (Ljava/lang/Object;)Z + public final fun getIndexOrNull (Ljava/lang/String;)Ljava/lang/Integer; + public fun getSimpleName ()Ljava/lang/String; + public fun getType ()Lcom/github/avrokotlin/avro4k/AvroSchema$Type; + public final fun getTypes ()Ljava/util/List; + public fun hashCode ()I + public final fun isNullable ()Z + public final fun isSimpleNullableType ()Z + public fun toString ()Ljava/lang/String; +} + +public final class com/github/avrokotlin/avro4k/AvroSchemaApacheInteropKt { + public static final fun fromApacheSchema (Lcom/github/avrokotlin/avro4k/AvroSchema$Companion;Lorg/apache/avro/Schema;)Lcom/github/avrokotlin/avro4k/AvroSchema; + public static final fun toApacheSchema (Lcom/github/avrokotlin/avro4k/AvroSchema;)Lorg/apache/avro/Schema; +} + +public final class com/github/avrokotlin/avro4k/AvroSchemaJsonDeserializerKt { + public static final fun fromJsonElement (Lcom/github/avrokotlin/avro4k/AvroSchema$Companion;Lkotlinx/serialization/json/JsonElement;Ljava/util/Map;)Lcom/github/avrokotlin/avro4k/AvroSchema; + public static synthetic fun fromJsonElement$default (Lcom/github/avrokotlin/avro4k/AvroSchema$Companion;Lkotlinx/serialization/json/JsonElement;Ljava/util/Map;ILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroSchema; +} + +public final class com/github/avrokotlin/avro4k/AvroSchemaJsonSerializerKt { + public static final fun toJsonElement (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Set;)Lkotlinx/serialization/json/JsonElement; + public static synthetic fun toJsonElement$default (Lcom/github/avrokotlin/avro4k/AvroSchema;Ljava/util/Set;ILjava/lang/Object;)Lkotlinx/serialization/json/JsonElement; +} + +public final class com/github/avrokotlin/avro4k/AvroSchemaKt { + public static final fun getLogicalTypeName (Lcom/github/avrokotlin/avro4k/ResolvedSchema;)Ljava/lang/String; + public static final fun getNullable (Lcom/github/avrokotlin/avro4k/AvroSchema;)Lcom/github/avrokotlin/avro4k/AvroSchema; + public static final fun isNullable (Lcom/github/avrokotlin/avro4k/AvroSchema;)Z +} + public final class com/github/avrokotlin/avro4k/AvroSingleObject : kotlinx/serialization/BinaryFormat { public fun (Lkotlin/jvm/functions/Function1;Lcom/github/avrokotlin/avro4k/Avro;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lcom/github/avrokotlin/avro4k/Avro;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -330,9 +648,23 @@ public final class com/github/avrokotlin/avro4k/MissingFieldsEncodingException : public fun (Ljava/util/List;Lorg/apache/avro/Schema;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } +public final class com/github/avrokotlin/avro4k/Name { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getFullName ()Ljava/lang/String; + public final fun getSimpleName ()Ljava/lang/String; + public final fun getSpace ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public abstract interface class com/github/avrokotlin/avro4k/Record : org/apache/avro/generic/GenericRecord, org/apache/avro/specific/SpecificRecord { } +public abstract class com/github/avrokotlin/avro4k/ResolvedSchema : com/github/avrokotlin/avro4k/AvroSchema, com/github/avrokotlin/avro4k/WithProps { +} + public abstract interface class com/github/avrokotlin/avro4k/ShortValueDecoder { public abstract fun decodeShort (Lcom/github/avrokotlin/avro4k/AvroDecoder;)S } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 19b7f813..112c1208 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,22 +1,111 @@ + +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithSimulatorTests +import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.HostManager + plugins { - id("library-module-conventions") + id("library-multiplatform-module-conventions") id("library-publish-conventions") kotlin("plugin.serialization") } description = "Core module of avro4k. Avro4k is the avro binary format support for kotlin, built on top of kotlinx-serialization." -dependencies { - api(libs.apache.avro) - api(libs.kotlinx.serialization.core) - api(libs.kotlinx.io) - implementation(libs.kotlinx.serialization.json) - implementation(libs.okio) - - testImplementation(libs.kotest.junit5) - testImplementation(libs.kotest.core) - testImplementation(libs.mockk) - testImplementation(kotlin("reflect")) +kotlin { + jvm() + + iosArm64() + iosSimulatorArm64() + macosArm64() + watchosSimulatorArm64() + watchosArm32() + watchosArm64() + tvosSimulatorArm64() + tvosArm64() + watchosDeviceArm64() + iosX64() + + androidNativeArm32() + androidNativeArm64() + androidNativeX86() + androidNativeX64() + + mingwX64() + + linuxArm64() + linuxX64() + + js(IR) { + browser() + nodejs() + } +// @OptIn(ExperimentalWasmDsl::class) +// wasmJs { browser() } + + sourceSets { + commonMain { + dependencies { + api(libs.kotlinx.serialization.core) + api(libs.kotlinx.serialization.json) + api(libs.kotlinx.io) + } + } + + commonTest { + dependencies { + implementation(kotlin("test")) + implementation(libs.kotest.core) + } + } + + jvmMain { + dependencies { + api(libs.apache.avro) + implementation(libs.okio) + implementation(libs.mockk) + } + } + + jvmTest { + dependencies { + implementation(libs.kotest.junit5) + implementation(kotlin("reflect")) + } + } + } +} + +// Skip Apple simulator test tasks when the required simulator runtime is not installed +run { + if (!HostManager.hostIsMac) return@run + val simulatorFamilyPrefixes = + mapOf( + Family.IOS to "iOS ", + Family.TVOS to "tvOS ", + Family.WATCHOS to "watchOS " + ) + val installedFamilies: Set = providers.exec { + isIgnoreExitValue = true + commandLine("xcrun", "simctl", "list", "runtimes") + }.let { execOutput -> + if (execOutput.result.orNull?.exitValue != 0) { + emptySet() + } else { + val lines = + execOutput.standardOutput.asText.get().lines() + simulatorFamilyPrefixes.filterValues { prefix -> lines.any { it.startsWith(prefix) } }.keys + } + } + + kotlin.testableTargets + .filterIsInstance() + .filter { it.konanTarget.family in simulatorFamilyPrefixes } + .forEach { target -> + val family = target.konanTarget.family + tasks.named("${target.name}Test") { + onlyIf("No $family simulator runtime installed") { family in installedFamilies } + } + } } spotless { diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt b/core/src/commonMain/kotlin/Annotations.kt similarity index 88% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt rename to core/src/commonMain/kotlin/Annotations.kt index 919ed1af..98a354d2 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt +++ b/core/src/commonMain/kotlin/Annotations.kt @@ -2,11 +2,8 @@ package com.github.avrokotlin.avro4k -import com.github.avrokotlin.avro4k.serializer.AvroSerializer -import com.github.avrokotlin.avro4k.serializer.BigDecimalSerializer import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialInfo -import org.intellij.lang.annotations.Language /** * Adds a property to the Avro schema or field. Its value could be any valid JSON or just a string. @@ -21,7 +18,7 @@ import org.intellij.lang.annotations.Language @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) public annotation class AvroProp( val key: String, - @Language("JSON") val value: String, + val value: String, ) /** @@ -70,21 +67,6 @@ public annotation class AvroAlias(vararg val value: String) @Target(AnnotationTarget.PROPERTY) public annotation class AvroStringable -/** - * To be used with [BigDecimalSerializer] to specify the scale, precision, type and rounding mode of the decimal value. - * - * Can be used with [AvroFixed] to serialize value as a fixed type. - * - * Only works with [java.math.BigDecimal] property type. - */ -@SerialInfo -@ExperimentalAvro4kApi -@Target(AnnotationTarget.PROPERTY) -public annotation class AvroDecimal( - val scale: Int, - val precision: Int, -) - /** * Indicates that the annotated property should be encoded as an Avro fixed type. * @@ -109,7 +91,7 @@ public annotation class AvroFixed(val size: Int) @SerialInfo @Target(AnnotationTarget.PROPERTY) public annotation class AvroDefault( - @Language("JSON") val value: String, + val value: String, ) /** diff --git a/core/src/commonMain/kotlin/AvroSchema.kt b/core/src/commonMain/kotlin/AvroSchema.kt new file mode 100644 index 00000000..d8c9c22e --- /dev/null +++ b/core/src/commonMain/kotlin/AvroSchema.kt @@ -0,0 +1,608 @@ +package com.github.avrokotlin.avro4k + +import com.github.avrokotlin.avro4k.AvroSchema.NullSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema +import com.github.avrokotlin.avro4k.internal.IdentitySet +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonPrimitive + +@InternalAvro4kApi +public interface WithProps { + public val props: Map +} + +@InternalAvro4kApi +public interface WithDoc { + public val doc: String? +} + +public val AvroSchema.isNullable: Boolean get() = this is NullSchema || (this is UnionSchema && isNullable) +public val AvroSchema.nullable: AvroSchema + get() { + if (isNullable) return this + return if (this is UnionSchema) { + UnionSchema(listOf(NullSchema()) + this.types) + } else { + UnionSchema(NullSchema(), this as ResolvedSchema) + } + } + +public val ResolvedSchema.logicalTypeName: String? + get() = props["logicalType"]?.jsonPrimitive?.contentOrNull + +/** + * This is a marker indicating that the schema is resolved, meaning it be of any type, except a [UnionSchema]. + * Any resolved schema can have properties on it. + * + * @see AvroSchema + * @see WithProps + */ +public sealed class ResolvedSchema : AvroSchema(), WithProps + +/** + * This class is the future drop-in multiplatform replacement for the Apache Avro Schema class. + * It is not in the core module as it is tested first here. Then it will be moved to core when ready. + */ +public sealed class AvroSchema { + public open val fullName: String get() = simpleName + public abstract val simpleName: String + public abstract val type: Type + + internal open fun equals(other: Any?, seen: IdentitySet): Boolean { + return equals(other) + } + + internal open fun hashCode(seen: IdentitySet): Int { + return hashCode() + } + + internal open fun toString(seen: IdentitySet): String { + return toString() + } + + public enum class Type { + RECORD, + ENUM, + FIXED, + ARRAY, + MAP, + UNION, + NULL, + BOOLEAN, + INT, + LONG, + FLOAT, + DOUBLE, + BYTES, + STRING, + } + + public sealed class PrimitiveSchema : ResolvedSchema() + + public data class BooleanSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "boolean" + override val type: Type + get() = Type.BOOLEAN + } + + public data class IntSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "int" + override val type: Type + get() = Type.INT + } + + public data class LongSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "long" + override val type: Type + get() = Type.LONG + } + + public data class FloatSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "float" + override val type: Type + get() = Type.FLOAT + } + + public data class DoubleSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "double" + override val type: Type + get() = Type.DOUBLE + } + + public data class BytesSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "bytes" + override val type: Type + get() = Type.BYTES + } + + public data class StringSchema( + override val props: Map = emptyMap(), + ) : PrimitiveSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "string" + override val type: Type + get() = Type.STRING + } + + public data class NullSchema( + override val props: Map = emptyMap(), + ) : ResolvedSchema() { + init { + ensureNotProhibitedProp("type") + } + + override val simpleName: String + get() = "null" + override val type: Type + get() = Type.NULL + } + + public data class UnionSchema( + val types: List, + ) : AvroSchema() { + val isNullable: Boolean = types.any { it is NullSchema } + val isSimpleNullableType: Boolean get() = types.size == 2 && isNullable + + private val typesIndexByFullName: Map = buildMap(types.size) { + iterateOverFullNamesIncludingAliases { fullName, index, _ -> put(fullName, index) } + } + + public fun getIndexOrNull(fullName: String): Int? = typesIndexByFullName[fullName] + + public constructor(vararg types: ResolvedSchema) : this(types.toList()) + + init { + require(types.isNotEmpty()) { "Union must have at least one type" } + require(types.associateBy { it.fullName }.size == types.size) { "Union cannot contain duplicate type full-names" } + + val similarTypeNames = buildMap(types.size) { + iterateOverFullNamesIncludingAliases { fullName, _, _ -> + val count = getOrPut(fullName) { 0 } + put(fullName, count + 1) + } + }.filterValues { it > 1 }.keys + require(similarTypeNames.isEmpty()) { "Similar type names or aliases found: $similarTypeNames" } + } + + override val simpleName: String + get() = "union" + override val type: Type + get() = Type.UNION + + override fun equals(other: Any?): Boolean { + return equals(other, IdentitySet()) + } + + override fun equals(other: Any?, seen: IdentitySet): Boolean { + if (this === other) return true + if (other !is UnionSchema) return false + if (types.size != other.types.size) return false + for (i in types.indices) { + if (!types[i].equals(other.types[i], seen)) return false + } + return true + } + + override fun hashCode(): Int { + return hashCode(IdentitySet()) + } + + override fun hashCode(seen: IdentitySet): Int { + return types.sumOf { it.hashCode(seen) } + } + + override fun toString(): String { + return toString(IdentitySet()) + } + + override fun toString(seen: IdentitySet): String { + return "${UnionSchema::class.simpleName}(types=[${types.joinToString { it.toString(seen) }}])" + } + + private inline fun iterateOverFullNamesIncludingAliases(crossinline onFullName: (fullName: String, index: Int, type: ResolvedSchema) -> Unit) { + for (index in types.indices) { + val type = types[index] + if (type is NamedSchema) { + for (alias in type.aliases) { + onFullName(alias.fullName, index, type) + } + } + onFullName(type.fullName, index, type) + } + } + } + + public data class ArraySchema( + val elementSchema: AvroSchema, + override val props: Map = emptyMap(), + ) : ResolvedSchema() { + init { + ensureNotProhibitedProp("type", "items") + } + + override val simpleName: String + get() = "array" + override val type: Type + get() = Type.ARRAY + + override fun equals(other: Any?): Boolean { + return equals(other, IdentitySet()) + } + + override fun equals(other: Any?, seen: IdentitySet): Boolean { + if (this === other) return true + if (other !is ArraySchema) return false + if (props != other.props) return false + return elementSchema.equals(other.elementSchema, seen) + } + + override fun hashCode(): Int { + return hashCode(IdentitySet()) + } + + override fun hashCode(seen: IdentitySet): Int { + return props.hashCode() + elementSchema.hashCode(seen) + } + + override fun toString(): String { + return toString(IdentitySet()) + } + + override fun toString(seen: IdentitySet): String { + return "${ArraySchema::class.simpleName}(props=$props, elementSchema=${elementSchema.toString(seen)})" + } + } + + public data class MapSchema( + val valueSchema: AvroSchema, + override val props: Map = emptyMap(), + ) : ResolvedSchema() { + init { + ensureNotProhibitedProp("type", "values") + } + + override val simpleName: String + get() = "map" + override val type: Type + get() = Type.MAP + + override fun equals(other: Any?): Boolean { + return equals(other, IdentitySet()) + } + + override fun equals(other: Any?, seen: IdentitySet): Boolean { + if (this === other) return true + if (other !is MapSchema) return false + if (props != other.props) return false + return valueSchema.equals(other.valueSchema, seen) + } + + override fun hashCode(): Int { + return hashCode(IdentitySet()) + } + + override fun hashCode(seen: IdentitySet): Int { + return props.hashCode() + valueSchema.hashCode(seen) + } + + override fun toString(): String { + return toString(IdentitySet()) + } + + override fun toString(seen: IdentitySet): String { + return "${MapSchema::class.simpleName}(props=$props, valueSchema=${valueSchema.toString(seen)})" + } + } + + public sealed class NamedSchema : ResolvedSchema(), WithDoc { + public abstract val name: Name + public abstract val aliases: Set + + override val fullName: String + get() = name.fullName + override val simpleName: String + get() = name.simpleName + } + + public data class RecordSchema( + override val name: Name, + val fields: List, + override val doc: String? = null, + override val aliases: Set = emptySet(), + override val props: Map = emptyMap(), + ) : NamedSchema() { + private lateinit var fieldsByName: Map + + init { + require(name !in aliases) { "Record name '$name' cannot be part of aliases $aliases" } + ensureNotProhibitedProp("type", "fields", "name", "namespace", "aliases", "doc") + if (fields is LockableList && !fields.isLocked) { + fields.onLock = ::validateFields + } else { + validateFields(fields) + } + } + + private fun validateFields(fields: List) { + fieldsByName = fields.flatMap { f -> listOf(f.name to f) + f.aliases.map { it to f } }.toMap() + require(fieldsByName.size == fields.size + fields.sumOf { it.aliases.size }) { "Record fields must be unique" } + } + + override val type: Type get() = Type.RECORD + + public fun getFieldByName(fieldName: String): Field { + return fieldsByName.getValue(fieldName) + } + + override fun equals(other: Any?): Boolean { + return equals(other, IdentitySet()) + } + + override fun equals(other: Any?, seen: IdentitySet): Boolean { + if (this === other) return true + if (other !is RecordSchema) return false + if (name != other.name) return false + if (props != other.props) return false + + if (fields.size != other.fields.size) return false + if (!seen.add(SeenPair(this, other))) + return true + for (i in fields.indices) { + if (!fields[i].equals(other.fields[i], seen)) return false + } + seen.remove(SeenPair(this, other)) + return true + } + + override fun hashCode(): Int { + return hashCode(IdentitySet()) + } + + override fun hashCode(seen: IdentitySet): Int { + if (!seen.add(this)) + return 0 + val hash = name.hashCode() + props.hashCode() + fields.sumOf { it.hashCode(seen) } + seen.remove(this) + return hash + } + + override fun toString(): String { + return toString(IdentitySet()) + } + + override fun toString(seen: IdentitySet): String { + if (!seen.add(this)) return "${RecordSchema::class.simpleName}(name=$name)" + return "${RecordSchema::class.simpleName}(name=$name, doc=$doc, aliases=$aliases, props=$props, fields=${fields.joinToString { it.toString(seen) }})" + } + + public data class Field( + val name: String, + val schema: AvroSchema, + val defaultValue: JsonElement? = null, + override val doc: String? = null, + val aliases: Set = emptySet(), + override val props: Map = emptyMap(), + ) : WithProps, WithDoc { + init { + require(name !in aliases) { "Field name '$name' cannot be part of aliases $aliases" } + ensureNotProhibitedProp("name", "type", "aliases", "doc", "default") + if (defaultValue != null) { + require(isValidDefault(defaultValue, schema)) { "'$defaultValue' is not a compatible default value for field '$name' with schema $schema" } + } + } + + internal fun equals(other: Field, seen: IdentitySet): Boolean { + if (this === other) return true + if (name != other.name) return false + if (defaultValue != other.defaultValue) return false + if (props != other.props) return false + + return schema.equals(other.schema, seen) + } + + internal fun hashCode(seen: IdentitySet): Int { + return name.hashCode() + (defaultValue?.hashCode() ?: 0) + props.hashCode() + schema.hashCode(seen) + } + + internal fun toString(seen: IdentitySet): String { + return "${Field::class.simpleName}(name=$name, defaultValue=$defaultValue, doc=$doc, aliases=$aliases, props=$props, schema=${schema.toString(seen)})" + } + + private fun isValidDefault(defaultValue: JsonElement, schema: AvroSchema): Boolean { + if (schema is UnionSchema) { + // A default value must be of type of the first one in a union + return defaultValue.isValidJsonForSchema(schema.types[0]) + } + return defaultValue.isValidJsonForSchema(schema) + } + } + } + + public data class FixedSchema( + override val name: Name, + val size: UInt, + override val doc: String? = null, + override val aliases: Set = emptySet(), + override val props: Map = emptyMap(), + ) : NamedSchema() { + init { + require(name !in aliases) { "Fixed name '$name' cannot be part of aliases $aliases" } + ensureNotProhibitedProp("type", "size", "name", "namespace", "aliases", "doc") + } + + override val type: Type get() = Type.FIXED + } + + public data class EnumSchema( + override val name: Name, + val symbols: List, + val defaultSymbol: String? = null, + override val doc: String? = null, + override val aliases: Set = emptySet(), + override val props: Map = emptyMap(), + ) : NamedSchema() { + init { + require(name !in aliases) { "Enum name '$name' cannot be part of aliases $aliases" } + require(symbols.toSet().size == symbols.size) { "Enum symbols must be unique" } + require(defaultSymbol == null || defaultSymbol in symbols) { "Default symbol must be one of the enum symbols" } + ensureNotProhibitedProp("type", "default", "symbols", "name", "namespace", "aliases", "doc") + } + + override val type: Type get() = Type.ENUM + } + + public companion object { + // placeholder to allow static extensions to be added like AvroSchema.fromJson() + } +} + +public class Name { + public val fullName: String + public val simpleName: String + public val space: String? + + public constructor(name: String, space: String? = null) { + val dot = name.lastIndexOf('.') + if (dot != -1) { + this.simpleName = name.substring(dot + 1) + this.space = name.substring(0, dot).ifEmpty { space }?.ifEmpty { null } + } else { + this.simpleName = name + this.space = space?.ifEmpty { null } + } + this.fullName = if (this.space == null) simpleName else "${this.space}.${simpleName}" + } + + override fun toString(): String { + return fullName + } + + override fun equals(other: Any?): Boolean { + if (other !is Name) return false + return fullName == other.fullName + } + + override fun hashCode(): Int { + return fullName.hashCode() + } +} + +private fun WithProps.ensureNotProhibitedProp(vararg prohibited: String) { + for (p in prohibited) { + require(p !in props) { "properties in ${this::class.simpleName} must not contain the field '$p'. Actual value: ${props[p]}" } + } +} + +internal class SeenPair( + val first: AvroSchema, + val second: AvroSchema, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is SeenPair) return false + return first === other.first && second === other.second + } + + override fun hashCode(): Int { + return first.hashCode() + second.hashCode() + } +} + +internal class LockableList : AbstractMutableList() { + private val list = mutableListOf() + var isLocked: Boolean = false + private set + lateinit var onLock: (List) -> Unit + + fun lock() { + ensureNotLocked() + isLocked = true + if (::onLock.isInitialized) onLock(this) + } + + override fun set(index: Int, element: T): T { + ensureNotLocked() + return list.set(index, element) + } + + override fun add(index: Int, element: T) { + ensureNotLocked() + list.add(index, element) + } + + override fun add(element: T): Boolean { + ensureNotLocked() + return list.add(element) + } + + override fun removeAt(index: Int): T { + ensureNotLocked() + return list.removeAt(index) + } + + override val size: Int + get() { + ensureLocked() + return list.size + } + + override fun get(index: Int): T { + ensureLocked() + return list[index] + } + + private fun ensureNotLocked() { + check(!isLocked) { "Cannot modify a locked list" } + } + + private fun ensureLocked() { + check(isLocked) { "Cannot access a non-locked list" } + } +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/AvroSchemaJsonDeserializer.kt b/core/src/commonMain/kotlin/AvroSchemaJsonDeserializer.kt new file mode 100644 index 00000000..5acba85d --- /dev/null +++ b/core/src/commonMain/kotlin/AvroSchemaJsonDeserializer.kt @@ -0,0 +1,230 @@ +@file:OptIn(ExperimentalSerializationApi::class) + +package com.github.avrokotlin.avro4k + +import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema +import com.github.avrokotlin.avro4k.AvroSchema.BooleanSchema +import com.github.avrokotlin.avro4k.AvroSchema.BytesSchema +import com.github.avrokotlin.avro4k.AvroSchema.DoubleSchema +import com.github.avrokotlin.avro4k.AvroSchema.EnumSchema +import com.github.avrokotlin.avro4k.AvroSchema.FixedSchema +import com.github.avrokotlin.avro4k.AvroSchema.FloatSchema +import com.github.avrokotlin.avro4k.AvroSchema.IntSchema +import com.github.avrokotlin.avro4k.AvroSchema.LongSchema +import com.github.avrokotlin.avro4k.AvroSchema.MapSchema +import com.github.avrokotlin.avro4k.AvroSchema.NullSchema +import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema +import com.github.avrokotlin.avro4k.AvroSchema.StringSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.booleanOrNull +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.doubleOrNull +import kotlinx.serialization.json.floatOrNull +import kotlinx.serialization.json.int +import kotlinx.serialization.json.intOrNull +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.longOrNull + +internal fun AvroSchema.Companion.fromJsonString( + json: String, + knownNamedTypes: MutableMap = mutableMapOf(), +): AvroSchema { + return fromJsonElement(Json.parseToJsonElement(json), null, knownNamedTypes) +} + +public fun AvroSchema.Companion.fromJsonElement( + element: JsonElement, + knownNamedTypes: MutableMap = mutableMapOf(), +): AvroSchema { + return fromJsonElement(element, null, knownNamedTypes) +} + +private fun fromJsonElement( + element: JsonElement, + currentSpace: String?, + knownNamedTypes: MutableMap, +): AvroSchema { + return when (element) { + is JsonPrimitive -> { + // Handle shorthand primitive type or referenced named type + when (val type = element.contentOrNull) { + "null" -> NullSchema() + + "boolean" -> BooleanSchema() + + "int" -> IntSchema() + + "long" -> LongSchema() + + "float" -> FloatSchema() + + "double" -> DoubleSchema() + + "bytes" -> BytesSchema() + + "string" -> StringSchema() + + null -> throw IllegalArgumentException("type cannot be null json literal") + + else -> + knownNamedTypes[Name(type, currentSpace).fullName] + ?: throw IllegalArgumentException("Unknown named type: $type") + } + } + + is JsonArray -> { + // Handle union + UnionSchema(element.map { + val type = fromJsonElement(it, currentSpace, knownNamedTypes) + if (type !is ResolvedSchema) { + throw IllegalArgumentException("Unions cannot contain nested unions") + } + type + }) + } + + is JsonObject -> { + val props = element.toMutableMap() + when (val type = props.removeMandatory("type").stringPrimitive) { + "null" -> NullSchema(props = props) + + "boolean" -> BooleanSchema(props = props) + + "int" -> IntSchema(props = props) + + "long" -> LongSchema(props = props) + + "float" -> FloatSchema(props = props) + + "double" -> DoubleSchema(props = props) + + "bytes" -> BytesSchema(props = props) + + "string" -> StringSchema(props = props) + + "map" -> + MapSchema( + valueSchema = fromJsonElement(props.removeMandatory("values"), currentSpace, knownNamedTypes), + props = props + ) + + "array" -> + ArraySchema( + elementSchema = fromJsonElement(props.removeMandatory("items"), currentSpace, knownNamedTypes), + props = props + ) + + "enum" -> { + val name = props.removeName(currentSpace) + EnumSchema( + name = name, + symbols = props.removeMandatory("symbols").jsonArray.map { it.stringPrimitive }, + defaultSymbol = props.remove("default")?.stringPrimitive, + doc = props.remove("doc")?.stringPrimitive, + aliases = props.removeAliases(name.space) ?: emptySet(), + props = props + ).also { knownNamedTypes[name.fullName] = it } + } + + "fixed" -> { + val name = props.removeName(currentSpace) + FixedSchema( + name = name, + size = props.removeMandatory("size").jsonPrimitive.int.toUInt(), + doc = props.remove("doc")?.stringPrimitive, + aliases = props.removeAliases(name.space) ?: emptySet(), + props = props + ).also { knownNamedTypes[name.fullName] = it } + } + + "record" -> { + val name = props.removeName(currentSpace) + val fieldsJson = props.removeMandatory("fields").jsonArray + val fields = LockableList() + val schema = RecordSchema( + name = name, + fields = fields, + doc = props.remove("doc")?.stringPrimitive, + aliases = props.removeAliases(name.space) ?: emptySet(), + props = props + ) + knownNamedTypes[name.fullName] = schema + fieldsJson.forEach { field -> + val fieldProps = field.jsonObject.toMutableMap() + fields += RecordSchema.Field( + name = fieldProps.removeMandatory("name").stringPrimitive, + schema = fromJsonElement(fieldProps.removeMandatory("type"), name.space, knownNamedTypes), + doc = fieldProps.remove("doc")?.stringPrimitive, + aliases = fieldProps.remove("aliases")?.jsonArray?.map { it.stringPrimitive }?.toSet() ?: emptySet(), + // TODO validate record's default, requiring all the fields to be present, and fixed default to be of expected size + defaultValue = fieldProps.remove("default"), + props = fieldProps + ) + } + fields.lock() + schema + } + + else -> throw IllegalArgumentException("Unknown schema type: $type") + } + } + } +} + +private fun MutableMap.removeAliases( + namespace: String?, +): Set? = remove("aliases")?.jsonArray?.map { Name(it.stringPrimitive, namespace) }?.toSet() + +private fun MutableMap.removeMandatory(key: K): V { + return this.remove(key) ?: throw IllegalArgumentException("Missing '$key' key") +} + +private fun MutableMap.removeName(defaultSpace: String?): Name { + val name = removeMandatory("name").stringPrimitive + val namespace = remove("namespace")?.stringPrimitive + return Name(name = name, space = namespace ?: defaultSpace) +} + +private val JsonElement.stringPrimitive: String + get() { + if (this !is JsonPrimitive || !isString) throw IllegalArgumentException("Not a string: $this") + return content + } + +internal fun JsonElement.isValidJsonForSchema(schema: AvroSchema): Boolean { + // logical type is not taken into account, as the spec indicates + // that the default value should be of the raw type. + return when (schema) { + is UnionSchema -> schema.types.any { isValidJsonForSchema(it) } + + is EnumSchema, + is FixedSchema, + is BytesSchema, + is StringSchema -> this is JsonPrimitive && this.isString + + is NullSchema -> this == JsonNull + is BooleanSchema -> this is JsonPrimitive && this.booleanOrNull != null + is IntSchema -> this is JsonPrimitive && this.intOrNull != null + is LongSchema -> this is JsonPrimitive && this.longOrNull != null + is FloatSchema -> this is JsonPrimitive && this.floatOrNull != null + is DoubleSchema -> this is JsonPrimitive && this.doubleOrNull != null + is ArraySchema -> this is JsonArray && this.all { it.isValidJsonForSchema(schema.elementSchema) } + is MapSchema -> this is JsonObject && this.all { it.value.isValidJsonForSchema(schema.valueSchema) } + is RecordSchema -> { + if (this !is JsonObject) false + else schema.fields.all { field -> + val fieldValue = this[field.name] ?: field.defaultValue + fieldValue != null && fieldValue.isValidJsonForSchema(field.schema) + } + } + } +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/AvroSchemaJsonSerializer.kt b/core/src/commonMain/kotlin/AvroSchemaJsonSerializer.kt new file mode 100644 index 00000000..7bc53abd --- /dev/null +++ b/core/src/commonMain/kotlin/AvroSchemaJsonSerializer.kt @@ -0,0 +1,126 @@ +@file:OptIn(ExperimentalSerializationApi::class) + +package com.github.avrokotlin.avro4k + +import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema +import com.github.avrokotlin.avro4k.AvroSchema.EnumSchema +import com.github.avrokotlin.avro4k.AvroSchema.FixedSchema +import com.github.avrokotlin.avro4k.AvroSchema.MapSchema +import com.github.avrokotlin.avro4k.AvroSchema.NamedSchema +import com.github.avrokotlin.avro4k.AvroSchema.NullSchema +import com.github.avrokotlin.avro4k.AvroSchema.PrimitiveSchema +import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObjectBuilder +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.addAll +import kotlinx.serialization.json.addJsonObject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put +import kotlinx.serialization.json.putJsonArray + +public fun AvroSchema.toJsonElement(knownNamedTypes: MutableSet = mutableSetOf()): JsonElement { + return toJsonElement(null, knownNamedTypes) +} + +private fun AvroSchema.toJsonElement(currentSpace: String? = null, knownNamedTypes: MutableSet = mutableSetOf()): JsonElement { + return when (this) { + is PrimitiveSchema, is NullSchema -> + if (props.isEmpty()) { + JsonPrimitive(fullName) + } else { + buildJsonObject { + put("type", fullName) + putAll(props) + } + } + + is UnionSchema -> JsonArray(types.map { it.toJsonElement(currentSpace, knownNamedTypes) }) + + is ArraySchema -> + buildJsonObject { + put("type", "array") + put("items", elementSchema.toJsonElement(currentSpace, knownNamedTypes)) + putAll(props) + } + + is MapSchema -> + buildJsonObject { + put("type", "map") + put("values", valueSchema.toJsonElement(currentSpace, knownNamedTypes)) + putAll(props) + } + + is NamedSchema -> { + if (fullName in knownNamedTypes) { + // Known types are inlined, as they are already defined earlier in the schema, or are provided by the user in another file by example + JsonPrimitive(if (name.space == currentSpace) name.simpleName else fullName) + } else { + knownNamedTypes.add(fullName) + when (this) { + is EnumSchema -> + buildJsonObject { + put("type", "enum") + putNamedSchemaProps(this@toJsonElement, currentSpace) + putJsonArray("symbols") { addAll(symbols) } + if (defaultSymbol != null) put("default", defaultSymbol) + putAll(props) + putAliases(aliases.map { if (name.space == it.space) it.simpleName else it.fullName }) + } + + is FixedSchema -> + buildJsonObject { + put("type", "fixed") + putNamedSchemaProps(this@toJsonElement, currentSpace) + put("size", size.toInt()) + putAll(props) + putAliases(aliases.map { if (name.space == it.space) it.simpleName else it.fullName }) + } + + is RecordSchema -> + buildJsonObject { + put("type", "record") + putNamedSchemaProps(this@toJsonElement, currentSpace) + putJsonArray("fields") { + fields.forEach { field -> + addJsonObject { + put("name", field.name) + put("type", field.schema.toJsonElement(name.space ?: currentSpace, knownNamedTypes)) + if (field.doc != null) put("doc", field.doc) + if (field.defaultValue != null) put("default", field.defaultValue) + putAliases(field.aliases) + putAll(field.props) + } + } + } + putAll(props) + putAliases(aliases.map { if (name.space == it.space) it.simpleName else it.fullName }) + } + } + } + } + } +} + +private fun JsonObjectBuilder.putNamedSchemaProps(schema: NamedSchema, currentSpace: String?) { + put("name", schema.name.simpleName) + if (schema.name.space != null) { + if (schema.name.space != currentSpace) { + put("namespace", schema.name.space) + } + } else if (currentSpace != null) { + put("namespace", "") + } + if (schema.doc != null) put("doc", schema.doc) +} + +private fun JsonObjectBuilder.putAliases(aliases: Collection) { + if (aliases.isNotEmpty()) putJsonArray("aliases") { addAll(aliases) } +} + +private fun JsonObjectBuilder.putAll(map: Map) { + map.forEach { (key, value) -> put(key, value) } +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/internal/IdentitySet.kt b/core/src/commonMain/kotlin/internal/IdentitySet.kt new file mode 100644 index 00000000..b4cf2d60 --- /dev/null +++ b/core/src/commonMain/kotlin/internal/IdentitySet.kt @@ -0,0 +1,6 @@ +package com.github.avrokotlin.avro4k.internal + +internal expect class IdentitySet() { + fun add(value: T): Boolean + fun remove(value: T): Boolean +} diff --git a/core/src/commonMain/kotlin/internal/WeakIdentityKeyCache.kt b/core/src/commonMain/kotlin/internal/WeakIdentityKeyCache.kt new file mode 100644 index 00000000..5929356c --- /dev/null +++ b/core/src/commonMain/kotlin/internal/WeakIdentityKeyCache.kt @@ -0,0 +1,9 @@ +package com.github.avrokotlin.avro4k.internal + +import com.github.avrokotlin.avro4k.InternalAvro4kApi + +@InternalAvro4kApi +public expect class WeakIdentityKeyCache() { + @InternalAvro4kApi + public fun getOrPut(key: K, defaultValue: () -> V): V +} \ No newline at end of file diff --git a/core/src/commonTest/kotlin/internal/IdentitySetTest.kt b/core/src/commonTest/kotlin/internal/IdentitySetTest.kt new file mode 100644 index 00000000..4b3b2b30 --- /dev/null +++ b/core/src/commonTest/kotlin/internal/IdentitySetTest.kt @@ -0,0 +1,90 @@ +package com.github.avrokotlin.avro4k.internal + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class IdentitySetTest { + @Test + fun `adding an item twice returns false`() { + val set = IdentitySet() + + assertTrue(set.add("foo")) + assertTrue(set.add("bar")) + + assertFalse(set.add("foo")) + } + + @Test + fun `adding equals items twice but in 2 different instances returns true`() { + data class Item(val value: String) + val set = IdentitySet() + assertTrue(set.add(Item("foo"))) + assertTrue(set.add(Item("foo"))) + } + + @Test + fun `removing an item twice returns true then false for the same item`() { + val set = IdentitySet() + set.add("foo") + set.add("bar") + + assertTrue(set.remove("foo")) + assertFalse(set.remove("foo")) + } + + @Test + fun `removing from empty set returns false`() { + val set = IdentitySet() + assertFalse(set.remove("foo")) + } + + @Test + fun `removing an item that was never added returns false`() { + val set = IdentitySet() + set.add("bar") + assertFalse(set.remove("foo")) + } + + @Test + fun `removing equal but different instance does not remove the original`() { + data class Item(val value: String) + val set = IdentitySet() + val item = Item("foo") + set.add(item) + + assertFalse(set.remove(Item("foo"))) + assertTrue(set.remove(item)) + } + + @Test + fun `re-adding after removal returns true`() { + val set = IdentitySet() + val item = "foo" + + assertTrue(set.add(item)) + assertTrue(set.remove(item)) + assertTrue(set.add(item)) + } + + @Test + fun `adding multiple distinct instances works independently`() { + val set = IdentitySet() + val a = "alpha" + val b = "beta" + val c = "gamma" + + assertTrue(set.add(a)) + assertTrue(set.add(b)) + assertTrue(set.add(c)) + + assertFalse(set.add(a)) + assertFalse(set.add(b)) + assertFalse(set.add(c)) + + assertTrue(set.remove(b)) + assertFalse(set.remove(b)) + assertFalse(set.add(a)) + assertTrue(set.add(b)) + } +} \ No newline at end of file diff --git a/core/src/jsMain/kotlin/internal/IdentitySet.js.kt b/core/src/jsMain/kotlin/internal/IdentitySet.js.kt new file mode 100644 index 00000000..2207b92d --- /dev/null +++ b/core/src/jsMain/kotlin/internal/IdentitySet.js.kt @@ -0,0 +1,17 @@ +package com.github.avrokotlin.avro4k.internal + +internal actual class IdentitySet { + private val backingSet: dynamic = js("new Set()") + + actual fun add(value: T): Boolean { + if (backingSet.has(value)) { + return false + } + backingSet.add(value) + return true + } + + actual fun remove(value: T): Boolean { + return backingSet.delete(value) + } +} \ No newline at end of file diff --git a/core/src/jsMain/kotlin/internal/WeakIdentityKeyCache.js.kt b/core/src/jsMain/kotlin/internal/WeakIdentityKeyCache.js.kt new file mode 100644 index 00000000..220c6688 --- /dev/null +++ b/core/src/jsMain/kotlin/internal/WeakIdentityKeyCache.js.kt @@ -0,0 +1,15 @@ +package com.github.avrokotlin.avro4k.internal + +import com.github.avrokotlin.avro4k.InternalAvro4kApi + + +@InternalAvro4kApi +public actual class WeakIdentityKeyCache { + @OptIn(ExperimentalWasmJsInterop::class) + private val map: dynamic = js("new WeakMap()") + + @InternalAvro4kApi + public actual fun getOrPut(key: K, defaultValue: () -> V): V { + return map.getOrInsertComputed(key, defaultValue) + } +} \ No newline at end of file diff --git a/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/Annotations.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/Annotations.kt new file mode 100644 index 00000000..cf6a98a3 --- /dev/null +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/Annotations.kt @@ -0,0 +1,22 @@ +@file:OptIn(ExperimentalSerializationApi::class) + +package com.github.avrokotlin.avro4k + +import com.github.avrokotlin.avro4k.serializer.BigDecimalSerializer +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerialInfo + +/** + * To be used with [BigDecimalSerializer] to specify the scale, precision, type and rounding mode of the decimal value. + * + * Can be used with [AvroFixed] to serialize value as a fixed type. + * + * Only works with [java.math.BigDecimal] property type. + */ +@SerialInfo +@ExperimentalAvro4kApi +@Target(AnnotationTarget.PROPERTY) +public annotation class AvroDecimal( + val scale: Int, + val precision: Int, +) \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/Avro.kt similarity index 98% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/Avro.kt index 1485f271..8f39d7c0 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/Avro.kt @@ -1,11 +1,10 @@ package com.github.avrokotlin.avro4k import com.github.avrokotlin.avro4k.internal.Buffer -import com.github.avrokotlin.avro4k.internal.Cache import com.github.avrokotlin.avro4k.internal.EnumResolver import com.github.avrokotlin.avro4k.internal.PolymorphicResolver import com.github.avrokotlin.avro4k.internal.RecordResolver -import com.github.avrokotlin.avro4k.internal.WeakKeyCache +import com.github.avrokotlin.avro4k.internal.WeakIdentityKeyCache import com.github.avrokotlin.avro4k.internal.schema.ValueVisitor import com.github.avrokotlin.avro4k.serializer.AnyTypeSerializersModule import com.github.avrokotlin.avro4k.serializer.JavaStdLibSerializersModule @@ -39,7 +38,7 @@ public sealed class Avro( public val configuration: AvroConfiguration, public final override val serializersModule: SerializersModule, ) : BinaryFormat { - private val schemaCache: Cache = WeakKeyCache() + private val schemaCache = WeakIdentityKeyCache() internal val recordResolver = RecordResolver(this) internal val polymorphicResolver = PolymorphicResolver(serializersModule) @@ -96,9 +95,9 @@ public sealed class Avro( */ public fun schema(descriptor: SerialDescriptor): Schema { return schemaCache.getOrPut(descriptor) { - lateinit var output: Schema + lateinit var output: AvroSchema ValueVisitor(this) { output = it }.visitValue(descriptor) - output + output.toApacheSchema() } } diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroGenericDataExtensions.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroGenericDataExtensions.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroGenericDataExtensions.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroGenericDataExtensions.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroJVMExtensions.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroJVMExtensions.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroJVMExtensions.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroJVMExtensions.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroKotlinxIoExtensions.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroKotlinxIoExtensions.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroKotlinxIoExtensions.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroKotlinxIoExtensions.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainer.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainer.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainer.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainer.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroOkioExtensions.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroOkioExtensions.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroOkioExtensions.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroOkioExtensions.kt diff --git a/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroSchemaApacheInterop.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroSchemaApacheInterop.kt new file mode 100644 index 00000000..a0d5205b --- /dev/null +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroSchemaApacheInterop.kt @@ -0,0 +1,20 @@ +package com.github.avrokotlin.avro4k + +import com.github.avrokotlin.avro4k.internal.WeakIdentityKeyCache +import kotlinx.serialization.json.Json +import org.apache.avro.NameValidator +import org.apache.avro.Schema +import org.apache.avro.SchemaFormatter + +private val newToApacheSchemaCache = WeakIdentityKeyCache() +private val apacheToNewSchemaCache = WeakIdentityKeyCache() + +public fun AvroSchema.Companion.fromApacheSchema(schema: Schema): AvroSchema = + apacheToNewSchemaCache.getOrPut(schema) { + AvroSchema.fromJsonString(SchemaFormatter.format("json", schema)) + } + +public fun AvroSchema.toApacheSchema(): Schema = newToApacheSchemaCache.getOrPut(this) { + val json = toJsonElement() + Schema.Parser(NameValidator.NO_VALIDATION).parse(Json.encodeToString(json)) +} diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSingleObject.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroSingleObject.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSingleObject.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/AvroSingleObject.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/ListRecord.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/ListRecord.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/ListRecord.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/ListRecord.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/AvroGenerated.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/AvroGenerated.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/AvroGenerated.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/AvroGenerated.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/AvroInternalExtensions.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/AvroInternalExtensions.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/AvroInternalExtensions.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/AvroInternalExtensions.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/EnumResolver.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/EnumResolver.kt similarity index 91% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/EnumResolver.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/EnumResolver.kt index 934ec2fe..ae5775aa 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/EnumResolver.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/EnumResolver.kt @@ -4,7 +4,7 @@ import com.github.avrokotlin.avro4k.AvroEnumDefault import kotlinx.serialization.descriptors.SerialDescriptor internal class EnumResolver { - private val defaultIndexCache: Cache = WeakKeyCache() + private val defaultIndexCache = WeakIdentityKeyCache() private data class EnumDefault(val index: Int?) diff --git a/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/IdentitySet.jvm.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/IdentitySet.jvm.kt new file mode 100644 index 00000000..7f93ffd4 --- /dev/null +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/IdentitySet.jvm.kt @@ -0,0 +1,16 @@ +package com.github.avrokotlin.avro4k.internal + +import java.util.Collections +import java.util.IdentityHashMap + +internal actual class IdentitySet { + private val backingSet: MutableSet = Collections.newSetFromMap(IdentityHashMap()) + + actual fun add(value: T): Boolean { + return backingSet.add(value) + } + + actual fun remove(value: T): Boolean { + return backingSet.remove(value) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/NumberUtils.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/NumberUtils.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/NumberUtils.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/NumberUtils.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/PolymorphicResolver.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/PolymorphicResolver.kt similarity index 91% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/PolymorphicResolver.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/PolymorphicResolver.kt index 462fa2db..c7f69e10 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/PolymorphicResolver.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/PolymorphicResolver.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.modules.SerializersModule internal class PolymorphicResolver(private val serializersModule: SerializersModule) { - private val cache = WeakKeyCache>() + private val cache = WeakIdentityKeyCache>() fun getFullNamesAndAliasesToSerialName(descriptor: SerialDescriptor): Map { return cache.getOrPut(descriptor) { diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt similarity index 98% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt index cc612adf..e0daeea9 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt @@ -28,7 +28,7 @@ internal class RecordResolver( * * Note: We use the descriptor in the key as we could have multiple descriptors for the same record schema, and multiple record schemas for the same descriptor. */ - private val fieldCache: Cache> = WeakKeyCache() + private val fieldCache: WeakIdentityKeyCache> = WeakIdentityKeyCache() /** * Maps the class fields to the schema fields. @@ -47,7 +47,7 @@ internal class RecordResolver( classDescriptor: SerialDescriptor, ): SerializationWorkflow { return fieldCache.getOrPut(writerSchema) { - WeakKeyCache() + WeakIdentityKeyCache() }.getOrPut(classDescriptor) { loadCache(classDescriptor, writerSchema) } diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/Samples.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/Samples.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/Samples.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/Samples.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/SerializerLocatorMiddleware.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/SerializerLocatorMiddleware.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/SerializerLocatorMiddleware.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/SerializerLocatorMiddleware.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/Cache.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/WeakIdentityKeyCache.jvm.kt similarity index 62% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/Cache.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/WeakIdentityKeyCache.jvm.kt index 3fbcbd5f..568c719d 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/Cache.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/WeakIdentityKeyCache.jvm.kt @@ -6,33 +6,20 @@ import java.lang.ref.WeakReference import java.util.concurrent.ConcurrentHashMap @InternalAvro4kApi -public interface Cache { - /** - * Returns the cached value, or computes, stores, and returns it. - * - * The value can be re-computed at any time. - */ - public fun getOrPut(key: K, compute: () -> V): V -} - -/** - * Cache for associating derived data with objects you don't own - * without preventing their garbage collection. - */ -@InternalAvro4kApi -public class WeakKeyCache : Cache { +public actual class WeakIdentityKeyCache { private val queue = ReferenceQueue() private val map = ConcurrentHashMap, V>() - override fun getOrPut(key: K, compute: () -> V): V { + @InternalAvro4kApi + public actual fun getOrPut(key: K, defaultValue: () -> V): V { removeStaleEntries() // "get" phase - val hash = key.hashCode() + val hash = System.identityHashCode(key) map[Key.Lookup(hash, key)]?.let { return it } // "put" phase - return map.computeIfAbsent(Key.Stored(hash, key, queue)) { compute() } + return map.computeIfAbsent(Key.Stored(hash, key, queue)) { defaultValue() } } private fun removeStaleEntries() { @@ -44,22 +31,22 @@ public class WeakKeyCache : Cache { } private sealed interface Key { + val value: K? class Stored(val hash: Int, value: K, queue: ReferenceQueue) : Key, WeakReference(value, queue) { override fun hashCode(): Int = hash + override val value: K? get() = get() override fun equals(other: Any?): Boolean { // The stale entries removed in removeStaleEntries() should be the same instance if (this === other) return true - - // If not removed, Stored keys are only compared against Stored keys during the "put" phase of getOrPut() - other as Stored<*> + if (other !is Key<*>) return false val a = get() - val b = other.get() + val b = other.value // Stale (null) entries are being removed, so they should not match if (a == null || b == null) return false - return a == b + return a === b } } @@ -67,17 +54,17 @@ public class WeakKeyCache : Cache { * Strong reference to the key to ensure the key isn't garbage collected during lookup and to * prevent a WeakReference to be created and referenced for potential queuing just for lookup. */ - class Lookup(val hash: Int, val value: K) : Key { + class Lookup(val hash: Int, override val value: K) : Key { override fun hashCode(): Int = hash override fun equals(other: Any?): Boolean { - // Lookup keys are only compared against Stored keys during the "get" phase of getOrPut() - other as Stored<*> + if (this === other) return true + if (other !is Key<*>) return false // Stale (null) entries are being removed, so they should not match - val otherValue = other.get() ?: return false + val otherValue = other.value ?: return false - return value == otherValue + return value === otherValue } } } diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/AbstractPolymorphicDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/AbstractPolymorphicDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/AbstractPolymorphicDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/AbstractPolymorphicDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractAvroDirectDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractAvroDirectDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractAvroDirectDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractAvroDirectDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractInterceptingDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractInterceptingDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractInterceptingDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AbstractInterceptingDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AvroValueDirectDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AvroValueDirectDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AvroValueDirectDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/AvroValueDirectDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/CollectionsDirectDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/CollectionsDirectDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/CollectionsDirectDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/CollectionsDirectDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/KotlinxIoDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/KotlinxIoDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/KotlinxIoDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/KotlinxIoDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordAsMapDirectDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordAsMapDirectDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordAsMapDirectDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordAsMapDirectDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AbstractAvroGenericDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AbstractAvroGenericDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AbstractAvroGenericDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AbstractAvroGenericDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AvroValueGenericDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AvroValueGenericDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AvroValueGenericDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/AvroValueGenericDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/CollectionGenericDecoders.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/CollectionGenericDecoders.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/CollectionGenericDecoders.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/CollectionGenericDecoders.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/PolymorphicGenericDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/PolymorphicGenericDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/PolymorphicGenericDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/PolymorphicGenericDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/RecordGenericDecoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/RecordGenericDecoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/RecordGenericDecoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/decoder/generic/RecordGenericDecoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/AbstractAvroEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/AbstractAvroEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/AbstractAvroEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/AbstractAvroEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/ReorderingCompositeEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/ReorderingCompositeEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/ReorderingCompositeEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/ReorderingCompositeEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/AbstractAvroDirectEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/AbstractAvroDirectEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/AbstractAvroDirectEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/AbstractAvroDirectEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/CollectionsEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/CollectionsEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/CollectionsEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/CollectionsEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/KotlinxIoEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/KotlinxIoEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/KotlinxIoEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/KotlinxIoEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/RecordDirectEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/RecordDirectEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/RecordDirectEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/direct/RecordDirectEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AbstractAvroGenericEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AbstractAvroGenericEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AbstractAvroGenericEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AbstractAvroGenericEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/ArrayGenericEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/ArrayGenericEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/ArrayGenericEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/ArrayGenericEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AvroValueGenericEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AvroValueGenericEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AvroValueGenericEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/AvroValueGenericEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/MapGenericEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/MapGenericEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/MapGenericEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/MapGenericEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/PolymorphicEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/PolymorphicEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/PolymorphicEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/PolymorphicEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/RecordGenericEncoder.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/RecordGenericEncoder.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/RecordGenericEncoder.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/encoder/generic/RecordGenericEncoder.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/exceptions.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/exceptions.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/exceptions.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/exceptions.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt similarity index 94% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt index 43bfce3a..9d2768c6 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.TextNode import com.github.avrokotlin.avro4k.AvroAlias +import com.github.avrokotlin.avro4k.AvroDefault import com.github.avrokotlin.avro4k.AvroProp import com.github.avrokotlin.avro4k.InternalAvro4kApi import kotlinx.io.Buffer @@ -18,6 +19,9 @@ import kotlinx.serialization.descriptors.elementDescriptors import kotlinx.serialization.descriptors.getContextualDescriptor import kotlinx.serialization.descriptors.getPolymorphicDescriptors import kotlinx.serialization.descriptors.nonNullOriginal +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.serializerOrNull import org.apache.avro.LogicalType @@ -213,6 +217,22 @@ internal val AvroProp.jsonNode: JsonNode return TextNode.valueOf(value) } +internal val AvroProp.jsonElement: JsonElement + get() { + if (value.isStartingAsJson()) { + return Json.parseToJsonElement(value) + } + return JsonPrimitive(value) + } + +internal val AvroDefault.jsonElement: JsonElement + get() { + if (value.isStartingAsJson()) { + return Json.parseToJsonElement(value) + } + return JsonPrimitive(value) + } + @OptIn(UnsafeIoApi::class) internal fun Buffer(bytes: ByteArray): Buffer = Buffer().apply { UnsafeBufferOperations.moveToTail(this, bytes) } \ No newline at end of file diff --git a/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt new file mode 100644 index 00000000..a4c79a7b --- /dev/null +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt @@ -0,0 +1,167 @@ +package com.github.avrokotlin.avro4k.internal.schema + +import com.github.avrokotlin.avro4k.AvroSchema +import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema +import com.github.avrokotlin.avro4k.AvroSchema.Type +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema +import com.github.avrokotlin.avro4k.LockableList +import com.github.avrokotlin.avro4k.Name +import com.github.avrokotlin.avro4k.ResolvedSchema +import com.github.avrokotlin.avro4k.internal.jsonElement +import com.github.avrokotlin.avro4k.internal.nonNullSerialName +import com.github.avrokotlin.avro4k.isNullable +import com.github.avrokotlin.avro4k.serializer.ElementLocation +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive + +internal class ClassVisitor( + descriptor: SerialDescriptor, + private val context: VisitorContext, + private val onSchemaBuilt: (AvroSchema) -> Unit, +) : SerialDescriptorClassVisitor { + private val fields = LockableList() + private val schemaAlreadyResolved: Boolean + private val schema: AvroSchema + + init { + var schemaAlreadyResolved = true + schema = + context.resolvedSchemas.getOrPut(descriptor.nonNullSerialName) { + schemaAlreadyResolved = false + + val annotations = TypeAnnotations(descriptor) + val schema = + RecordSchema( + name = Name(descriptor.nonNullSerialName), + doc = annotations.doc?.value, + aliases = annotations.aliases?.value?.map { Name(it) }?.toSet() ?: emptySet(), + props = annotations.props.associate { it.key to it.jsonElement }, + fields = fields + ) + schema + } + this.schemaAlreadyResolved = schemaAlreadyResolved + } + + override fun visitClassElement( + descriptor: SerialDescriptor, + elementIndex: Int, + ): SerialDescriptorValueVisitor? { + if (schemaAlreadyResolved) { + return null + } + return ValueVisitor(context.copy(inlinedElements = listOf(ElementLocation(descriptor, elementIndex)))) { fieldSchema -> + fields.add( + createField( + context.avro.configuration.fieldNamingStrategy.resolve(descriptor, elementIndex), + FieldAnnotations(descriptor, elementIndex), + fieldSchema + ) + ) + } + } + + override fun endClassVisit(descriptor: SerialDescriptor) { + fields.lock() + onSchemaBuilt(schema) + } + + /** + * Create a field with the given annotations. + * Here are managed the generic field level annotations: + * - namespaceOverride + * - default (also sort unions according to the default value) + * - aliases + * - doc + * - props & json props + */ + private fun createField( + fieldName: String, + annotations: FieldAnnotations, + elementSchema: AvroSchema, + ): RecordSchema.Field { + val (finalSchema, fieldDefault) = getDefaultAndReorderUnionIfNeeded(annotations, elementSchema) + + return RecordSchema.Field( + name = fieldName, + schema = finalSchema, + doc = annotations.doc?.value, + defaultValue = fieldDefault, + aliases = annotations.aliases?.value?.toSet() ?: emptySet(), + props = annotations.props.associate { it.key to it.jsonElement } + ) + } + + private fun getDefaultAndReorderUnionIfNeeded( + annotations: FieldAnnotations, + elementSchema: AvroSchema, + ): Pair { + val defaultValue = annotations.default?.jsonElement + if (defaultValue == null) { + // No default value, let's make implicit default + if (context.configuration.implicitNulls && elementSchema.isNullable) { + return elementSchema.moveToHeadOfUnion { it.type == Type.NULL } to JsonNull + } else if (context.configuration.implicitEmptyCollections) { + (if (elementSchema is UnionSchema) elementSchema.types else listOf(elementSchema)).forEachIndexed { index, schema -> + if (schema.type == Type.ARRAY) { + return elementSchema.moveToHeadOfUnion(index) to JsonArray(emptyList()) + } + if (schema.type == Type.MAP) { + return elementSchema.moveToHeadOfUnion(index) to JsonObject(emptyMap()) + } + } + } + } else if (defaultValue === JsonNull) { + // If the user sets "null" but the field is not nullable, maybe the user wanted to set the "null" string default + val finalSchema = elementSchema.moveToHeadOfUnion { it.type == Type.NULL } + val adaptedDefault = if (!elementSchema.isNullable) JsonPrimitive("null") else defaultValue + return finalSchema to adaptedDefault + } else if (elementSchema.isNullable) { + // default is not null, so let's just put the null schema at the end of the union which should cover the main use cases + return elementSchema.moveToTailOfUnion { it.type === Type.NULL } to defaultValue + } + return elementSchema to defaultValue + } +} + +private fun AvroSchema.moveToHeadOfUnion(predicate: (ResolvedSchema) -> Boolean): AvroSchema { + if (this !is UnionSchema) { + return this + } + types.indexOfFirst(predicate).let { index -> + if (index == -1) { + return this + } + return moveToHeadOfUnion(index) + } +} + +private fun AvroSchema.moveToHeadOfUnion(typeIndex: Int): AvroSchema { + if (this !is UnionSchema || typeIndex >= types.size) { + return this + } + return UnionSchema(types.toMutableList().apply { add(0, removeAt(typeIndex)) }) +} + +private fun AvroSchema.moveToTailOfUnion(predicate: (AvroSchema) -> Boolean): AvroSchema { + if (this !is UnionSchema) { + return this + } + types.indexOfFirst(predicate).let { index -> + if (index == -1) { + return this + } + return moveToTailOfUnion(index) + } +} + +private fun AvroSchema.moveToTailOfUnion(typeIndex: Int): AvroSchema { + if (this !is UnionSchema || typeIndex >= types.size) { + return this + } + return UnionSchema(types.toMutableList().apply { add(removeAt(typeIndex)) }) +} \ No newline at end of file diff --git a/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/InlineClassVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/InlineClassVisitor.kt new file mode 100644 index 00000000..2c1c30f2 --- /dev/null +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/InlineClassVisitor.kt @@ -0,0 +1,75 @@ +package com.github.avrokotlin.avro4k.internal.schema + +import com.github.avrokotlin.avro4k.AvroSchema +import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema +import com.github.avrokotlin.avro4k.AvroSchema.BooleanSchema +import com.github.avrokotlin.avro4k.AvroSchema.BytesSchema +import com.github.avrokotlin.avro4k.AvroSchema.DoubleSchema +import com.github.avrokotlin.avro4k.AvroSchema.EnumSchema +import com.github.avrokotlin.avro4k.AvroSchema.FixedSchema +import com.github.avrokotlin.avro4k.AvroSchema.FloatSchema +import com.github.avrokotlin.avro4k.AvroSchema.IntSchema +import com.github.avrokotlin.avro4k.AvroSchema.LongSchema +import com.github.avrokotlin.avro4k.AvroSchema.MapSchema +import com.github.avrokotlin.avro4k.AvroSchema.NamedSchema +import com.github.avrokotlin.avro4k.AvroSchema.NullSchema +import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema +import com.github.avrokotlin.avro4k.AvroSchema.StringSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema +import com.github.avrokotlin.avro4k.internal.jsonElement +import com.github.avrokotlin.avro4k.serializer.AvroSerializer +import com.github.avrokotlin.avro4k.serializer.ElementLocation +import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.json.JsonElement + +internal class InlineClassVisitor( + private val context: VisitorContext, + private val onSchemaBuilt: (AvroSchema) -> Unit, +) : SerialDescriptorInlineClassVisitor { + override fun visitInlineClassElement( + inlineClassDescriptor: SerialDescriptor, + inlineElementIndex: Int, + ): SerialDescriptorValueVisitor { + val inlinedElements = context.inlinedElements + ElementLocation(inlineClassDescriptor, inlineElementIndex) + return ValueVisitor(context.copy(inlinedElements = inlinedElements)) { generatedSchema -> + val annotations = InlineClassFieldAnnotations(inlineClassDescriptor) + + val props = annotations.props.toList() + val schema = + if (props.isNotEmpty()) { + if (generatedSchema is NamedSchema) { + throw SerializationException( + "The value class property '${inlineClassDescriptor.serialName}.${inlineClassDescriptor.getElementName(0)}' has " + + "forbidden additional properties $props for the named schema ${generatedSchema.fullName}. " + + "Please create your own serializer extending ${AvroSerializer::class.qualifiedName} to add properties to a named schema." + ) + } + generatedSchema.withAdditionalProperties(props.associate { it.key to it.jsonElement }) + } else { + generatedSchema + } + + onSchemaBuilt(schema) + } + } +} + +private fun AvroSchema.withAdditionalProperties(additionalProps: Map): AvroSchema { + return when (this) { + is ArraySchema -> copy(props = props + additionalProps) + is MapSchema -> copy(props = props + additionalProps) + is EnumSchema -> copy(props = props + additionalProps) + is FixedSchema -> copy(props = props + additionalProps) + is RecordSchema -> copy(props = props + additionalProps) + is NullSchema -> copy(props = props + additionalProps) + is BooleanSchema -> copy(props = props + additionalProps) + is BytesSchema -> copy(props = props + additionalProps) + is DoubleSchema -> copy(props = props + additionalProps) + is FloatSchema -> copy(props = props + additionalProps) + is IntSchema -> copy(props = props + additionalProps) + is LongSchema -> copy(props = props + additionalProps) + is StringSchema -> copy(props = props + additionalProps) + is UnionSchema -> throw IllegalStateException("UnionSchema does not have properties") + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ListVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ListVisitor.kt similarity index 66% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ListVisitor.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ListVisitor.kt index 2493e79c..b02be53f 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ListVisitor.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ListVisitor.kt @@ -1,13 +1,14 @@ package com.github.avrokotlin.avro4k.internal.schema +import com.github.avrokotlin.avro4k.AvroSchema +import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema import kotlinx.serialization.descriptors.SerialDescriptor -import org.apache.avro.Schema internal class ListVisitor( private val context: VisitorContext, - private val onSchemaBuilt: (Schema) -> Unit, + private val onSchemaBuilt: (AvroSchema) -> Unit, ) : SerialDescriptorListVisitor { - private lateinit var itemSchema: Schema + private lateinit var itemSchema: AvroSchema override fun visitListItem( listDescriptor: SerialDescriptor, @@ -19,6 +20,6 @@ internal class ListVisitor( } override fun endListVisit(descriptor: SerialDescriptor) { - onSchemaBuilt(Schema.createArray(itemSchema)) + onSchemaBuilt(ArraySchema(itemSchema)) } } \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/MapVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/MapVisitor.kt similarity index 55% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/MapVisitor.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/MapVisitor.kt index ea8e32ec..f9711565 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/MapVisitor.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/MapVisitor.kt @@ -1,14 +1,16 @@ package com.github.avrokotlin.avro4k.internal.schema +import com.github.avrokotlin.avro4k.AvroSchema +import com.github.avrokotlin.avro4k.AvroSchema.MapSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema import com.github.avrokotlin.avro4k.internal.AvroSchemaGenerationException import kotlinx.serialization.descriptors.SerialDescriptor -import org.apache.avro.Schema internal class MapVisitor( private val context: VisitorContext, - private val onSchemaBuilt: (Schema) -> Unit, + private val onSchemaBuilt: (AvroSchema) -> Unit, ) : SerialDescriptorMapVisitor { - private lateinit var valueSchema: Schema + private lateinit var valueSchema: AvroSchema override fun visitMapKey( mapDescriptor: SerialDescriptor, @@ -20,10 +22,7 @@ internal class MapVisitor( // stringify (e.g. when .toString() makes sense). // Here we are just checking if the schema is string-compatible. We don't need to // store the schema as it is always a string. - if (it.isNullable()) { - throw AvroSchemaGenerationException("Map key cannot be nullable. Actual generated map key schema: $it") - } - if (!it.isNonNullScalarType()) { + if (!it.isAcceptedMapKeyType()) { throw AvroSchemaGenerationException("Map key must be a non-null scalar type (e.g. not a record, map or array). Actual generated map key schema: $it") } } @@ -36,29 +35,30 @@ internal class MapVisitor( } override fun endMapVisit(descriptor: SerialDescriptor) { - onSchemaBuilt(Schema.createMap(valueSchema)) + onSchemaBuilt(MapSchema(valueSchema)) } } -internal fun Schema.isNonNullScalarType(): Boolean = +internal fun AvroSchema.isAcceptedMapKeyType(): Boolean = + isNonNullScalarType() +private fun AvroSchema.isNonNullScalarType(): Boolean = when (type) { - Schema.Type.BOOLEAN, - Schema.Type.INT, - Schema.Type.LONG, - Schema.Type.FLOAT, - Schema.Type.DOUBLE, - Schema.Type.STRING, - Schema.Type.ENUM, - Schema.Type.BYTES, - Schema.Type.FIXED, - -> true + AvroSchema.Type.BOOLEAN, + AvroSchema.Type.INT, + AvroSchema.Type.LONG, + AvroSchema.Type.FLOAT, + AvroSchema.Type.DOUBLE, + AvroSchema.Type.STRING, + AvroSchema.Type.ENUM, + AvroSchema.Type.BYTES, + AvroSchema.Type.FIXED, + -> true - Schema.Type.NULL, - Schema.Type.ARRAY, - Schema.Type.MAP, - Schema.Type.RECORD, - null, - -> false + AvroSchema.Type.NULL, + AvroSchema.Type.ARRAY, + AvroSchema.Type.MAP, + AvroSchema.Type.RECORD, + -> false - Schema.Type.UNION -> types.all { it.isNonNullScalarType() } + AvroSchema.Type.UNION -> (this as UnionSchema).types.all { it.isNonNullScalarType() } } \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt similarity index 69% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt index adcf20b9..a4ad6a0c 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt @@ -1,18 +1,20 @@ package com.github.avrokotlin.avro4k.internal.schema +import com.github.avrokotlin.avro4k.AvroSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema +import com.github.avrokotlin.avro4k.ResolvedSchema import com.github.avrokotlin.avro4k.internal.AvroSchemaGenerationException import kotlinx.serialization.descriptors.SerialDescriptor -import org.apache.avro.Schema internal class PolymorphicVisitor( private val context: VisitorContext, - private val onSchemaBuilt: (Schema) -> Unit, + private val onSchemaBuilt: (AvroSchema) -> Unit, ) : SerialDescriptorPolymorphicVisitor { - private val possibleSchemas = mutableListOf() + private val possibleSchemas = mutableListOf() override fun visitPolymorphicFoundDescriptor(descriptor: SerialDescriptor): SerialDescriptorValueVisitor { return ValueVisitor(context) { - possibleSchemas += it + possibleSchemas += it as ResolvedSchema } } @@ -24,7 +26,7 @@ internal class PolymorphicVisitor( // flatten the useless union schema onSchemaBuilt(possibleSchemas.first()) } else { - onSchemaBuilt(Schema.createUnion(possibleSchemas)) + onSchemaBuilt(UnionSchema(possibleSchemas)) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/SerialDescriptorVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/SerialDescriptorVisitor.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/SerialDescriptorVisitor.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/SerialDescriptorVisitor.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ValueVisitor.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ValueVisitor.kt similarity index 62% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ValueVisitor.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ValueVisitor.kt index 55fc257a..32d3058f 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ValueVisitor.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/ValueVisitor.kt @@ -1,12 +1,24 @@ package com.github.avrokotlin.avro4k.internal.schema import com.github.avrokotlin.avro4k.Avro +import com.github.avrokotlin.avro4k.AvroSchema +import com.github.avrokotlin.avro4k.AvroSchema.BooleanSchema +import com.github.avrokotlin.avro4k.AvroSchema.DoubleSchema +import com.github.avrokotlin.avro4k.AvroSchema.EnumSchema +import com.github.avrokotlin.avro4k.AvroSchema.FloatSchema +import com.github.avrokotlin.avro4k.AvroSchema.IntSchema +import com.github.avrokotlin.avro4k.AvroSchema.LongSchema +import com.github.avrokotlin.avro4k.AvroSchema.StringSchema +import com.github.avrokotlin.avro4k.Name +import com.github.avrokotlin.avro4k.fromApacheSchema +import com.github.avrokotlin.avro4k.fromJsonString import com.github.avrokotlin.avro4k.internal.AvroGenerated import com.github.avrokotlin.avro4k.internal.SerializerLocatorMiddleware import com.github.avrokotlin.avro4k.internal.findAnnotation -import com.github.avrokotlin.avro4k.internal.jsonNode +import com.github.avrokotlin.avro4k.internal.jsonElement import com.github.avrokotlin.avro4k.internal.nonNullSerialName -import com.github.avrokotlin.avro4k.internal.nullable +import com.github.avrokotlin.avro4k.isNullable +import com.github.avrokotlin.avro4k.nullable import com.github.avrokotlin.avro4k.serializer.AvroSchemaSupplier import com.github.avrokotlin.avro4k.serializer.stringable import kotlinx.serialization.ExperimentalSerializationApi @@ -14,21 +26,19 @@ import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.nonNullOriginal +import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.modules.SerializersModule -import org.apache.avro.LogicalType -import org.apache.avro.Schema -import org.apache.avro.SchemaBuilder internal class ValueVisitor internal constructor( private val context: VisitorContext, - private val onSchemaBuilt: (Schema) -> Unit, + private val onSchemaBuilt: (AvroSchema) -> Unit, ) : SerialDescriptorValueVisitor { private var isNullable: Boolean = false override val serializersModule: SerializersModule get() = context.avro.serializersModule - constructor(avro: Avro, onSchemaBuilt: (Schema) -> Unit) : this( + constructor(avro: Avro, onSchemaBuilt: (AvroSchema) -> Unit) : this( VisitorContext( avro, mutableMapOf() @@ -44,14 +54,14 @@ internal class ValueVisitor internal constructor( override fun visitEnum(descriptor: SerialDescriptor) { val annotations = TypeAnnotations(descriptor) - val schema = - SchemaBuilder.enumeration(descriptor.nonNullSerialName) - .doc(annotations.doc?.value) - .defaultSymbol(context.avro.enumResolver.getDefaultValueIndex(descriptor)?.let { descriptor.getElementName(it) }) - .symbols(*descriptor.elementNamesArray) - - annotations.aliases?.value?.forEach { schema.addAlias(it) } - annotations.props.forEach { schema.addProp(it.key, it.jsonNode) } + val schema = EnumSchema( + name = Name(descriptor.nonNullSerialName), + symbols = descriptor.elementNamesArray.toList(), + defaultSymbol = context.avro.enumResolver.getDefaultValueIndex(descriptor)?.let { descriptor.getElementName(it) }, + doc = annotations.doc?.value, + aliases = annotations.aliases?.value?.map { Name(it) }?.toSet() ?: emptySet(), + props = annotations.props.associate { it.key to it.jsonElement }, + ) setSchema(schema) } @@ -78,7 +88,7 @@ internal class ValueVisitor internal constructor( override fun visitInlineClass(descriptor: SerialDescriptor) = InlineClassVisitor(context) { setSchema(it) } - private fun setSchema(schema: Schema) { + private fun setSchema(schema: AvroSchema) { if (isNullable && !schema.isNullable) { onSchemaBuilt(schema.nullable) } else { @@ -90,17 +100,17 @@ internal class ValueVisitor internal constructor( val finalDescriptor = SerializerLocatorMiddleware.apply(unwrapNullable(descriptor)) descriptor.findAnnotation()?.let { // Ignore everything and use the provided schema - setSchema(Schema.Parser().parse(it.originalSchema)) + setSchema(AvroSchema.fromJsonString(it.originalSchema)) return } if (finalDescriptor is AvroSchemaSupplier) { - setSchema(finalDescriptor.getSchema(context)) + setSchema(AvroSchema.fromApacheSchema(finalDescriptor.getSchema(context))) return } if (context.inlinedElements.any { it.stringable != null }) { - setSchema(Schema.create(Schema.Type.STRING)) + setSchema(StringSchema()) return } @@ -118,17 +128,16 @@ internal class ValueVisitor internal constructor( } internal const val CHAR_LOGICAL_TYPE_NAME = "char" -private val CHAR_LOGICAL_TYPE = LogicalType(CHAR_LOGICAL_TYPE_NAME) -private fun PrimitiveKind.toSchema(): Schema = +private fun PrimitiveKind.toSchema(): AvroSchema = when (this) { - PrimitiveKind.BOOLEAN -> Schema.create(Schema.Type.BOOLEAN) - PrimitiveKind.CHAR -> Schema.create(Schema.Type.INT).also { CHAR_LOGICAL_TYPE.addToSchema(it) } - PrimitiveKind.BYTE -> Schema.create(Schema.Type.INT) - PrimitiveKind.SHORT -> Schema.create(Schema.Type.INT) - PrimitiveKind.INT -> Schema.create(Schema.Type.INT) - PrimitiveKind.LONG -> Schema.create(Schema.Type.LONG) - PrimitiveKind.FLOAT -> Schema.create(Schema.Type.FLOAT) - PrimitiveKind.DOUBLE -> Schema.create(Schema.Type.DOUBLE) - PrimitiveKind.STRING -> Schema.create(Schema.Type.STRING) + PrimitiveKind.BOOLEAN -> BooleanSchema() + PrimitiveKind.CHAR -> IntSchema(props = mapOf("logicalType" to JsonPrimitive(CHAR_LOGICAL_TYPE_NAME))) + PrimitiveKind.BYTE -> IntSchema() + PrimitiveKind.SHORT -> IntSchema() + PrimitiveKind.INT -> IntSchema() + PrimitiveKind.LONG -> LongSchema() + PrimitiveKind.FLOAT -> FloatSchema() + PrimitiveKind.DOUBLE -> DoubleSchema() + PrimitiveKind.STRING -> StringSchema() } \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/VisitorContext.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/VisitorContext.kt similarity index 97% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/VisitorContext.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/VisitorContext.kt index 065f88dc..f7580330 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/VisitorContext.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/internal/schema/VisitorContext.kt @@ -6,6 +6,7 @@ import com.github.avrokotlin.avro4k.AvroConfiguration import com.github.avrokotlin.avro4k.AvroDefault import com.github.avrokotlin.avro4k.AvroDoc import com.github.avrokotlin.avro4k.AvroProp +import com.github.avrokotlin.avro4k.AvroSchema import com.github.avrokotlin.avro4k.internal.findAnnotation import com.github.avrokotlin.avro4k.internal.findAnnotations import com.github.avrokotlin.avro4k.internal.findElementAnnotation @@ -15,11 +16,10 @@ import com.github.avrokotlin.avro4k.serializer.SchemaSupplierContext import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.SerialKind import kotlinx.serialization.descriptors.StructureKind -import org.apache.avro.Schema internal data class VisitorContext( val avro: Avro, - val resolvedSchemas: MutableMap, + val resolvedSchemas: MutableMap, override val inlinedElements: List = emptyList(), ) : SchemaSupplierContext { override val configuration: AvroConfiguration diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AnySerializer.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AnySerializer.kt similarity index 98% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AnySerializer.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AnySerializer.kt index a951e4e4..3169ac61 100644 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AnySerializer.kt +++ b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AnySerializer.kt @@ -2,7 +2,7 @@ package com.github.avrokotlin.avro4k.serializer import com.github.avrokotlin.avro4k.AvroConfiguration import com.github.avrokotlin.avro4k.InternalAvro4kApi -import com.github.avrokotlin.avro4k.internal.WeakKeyCache +import com.github.avrokotlin.avro4k.internal.WeakIdentityKeyCache import com.github.avrokotlin.avro4k.internal.decoder.direct.AbstractAvroDirectDecoder import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.ExperimentalSerializationApi @@ -45,7 +45,7 @@ internal val AnyTypeSerializersModule: SerializersModule public open class AnySerializer : KSerializer { // No need to use a WeakHashMap with class keys private val encodingCache = ConcurrentHashMap, SerializationStrategy>() - private val decodingCache = WeakKeyCache>() + private val decodingCache = WeakIdentityKeyCache>() /** * Provides the nullable version of this [AnySerializer]. diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AvroDuration.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AvroDuration.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AvroDuration.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AvroDuration.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AvroSerializer.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AvroSerializer.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/AvroSerializer.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/AvroSerializer.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaTimeSerializers.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/JavaTimeSerializers.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaTimeSerializers.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/JavaTimeSerializers.kt diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/KotlinStdLibrarySerializers.kt b/core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/KotlinStdLibrarySerializers.kt similarity index 100% rename from core/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/KotlinStdLibrarySerializers.kt rename to core/src/jvmMain/kotlin/com/github/avrokotlin/avro4k/serializer/KotlinStdLibrarySerializers.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/AvroAssertions.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/AvroAssertions.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/AvroAssertions.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/AvroAssertions.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainerTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainerTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainerTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/AvroObjectContainerTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/AvroSingleObjectTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/AvroSingleObjectTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/AvroSingleObjectTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/AvroSingleObjectTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/KotlinxIoEncoderTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/KotlinxIoEncoderTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/KotlinxIoEncoderTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/KotlinxIoEncoderTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/RecordBuilderForTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/RecordBuilderForTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/RecordBuilderForTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/RecordBuilderForTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/dataClassesForTests.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/dataClassesForTests.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/dataClassesForTests.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/dataClassesForTests.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AnySerializerTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AnySerializerTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AnySerializerTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AnySerializerTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/ArrayEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/ArrayEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/ArrayEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/ArrayEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroAliasEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroAliasEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroAliasEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroAliasEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt similarity index 98% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt index 3502b33f..df0420e7 100644 --- a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt +++ b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt @@ -78,9 +78,9 @@ internal class AvroDefaultEncodingTest : StringSpec({ val shouldBe1AndNot42: Int = 42, @AvroDefault("true") val booleanDefault: Boolean, - @AvroDefault("a") + @AvroDefault("97") val charDefault: Char, - @AvroDefault("a") + @AvroDefault("97") val charDefaultNullable: Char?, @AvroDefault("null") val charDefaultNullableNull: Char?, diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroFixedEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroFixedEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroFixedEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroFixedEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroStringableEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroStringableEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroStringableEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/AvroStringableEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/CustomAvroSerializerTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/CustomAvroSerializerTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/CustomAvroSerializerTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/CustomAvroSerializerTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/EnumTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/EnumTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/EnumTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/EnumTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidDecodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidDecodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidDecodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidDecodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/KotlinUuidEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt similarity index 99% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt index 723316ac..e44cb79a 100644 --- a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt +++ b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt @@ -41,7 +41,6 @@ internal class LogicalTypesEncodingTest : StringSpec({ } "support non-nullable logical types" { - println(Avro.schema()) AvroAssertions.assertThat( LogicalTypes( BigDecimal("123.45"), diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/NestedClassEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/NestedClassEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/NestedClassEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/NestedClassEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/PrimitiveEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/PrimitiveEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/PrimitiveEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/PrimitiveEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordDecodingCompatibilityTests.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/RecordDecodingCompatibilityTests.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordDecodingCompatibilityTests.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/RecordDecodingCompatibilityTests.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/SealedClassEncodingTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/SealedClassEncodingTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/SealedClassEncodingTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/SealedClassEncodingTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/ValueClassTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/ValueClassTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/ValueClassTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/encoding/ValueClassTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ArraySchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/ArraySchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ArraySchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/ArraySchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroAliasSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroAliasSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroAliasSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroAliasSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroCustomSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroCustomSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroCustomSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroCustomSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt similarity index 97% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt index 9e302a98..5da4a5fb 100644 --- a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt +++ b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt @@ -9,7 +9,6 @@ import com.github.avrokotlin.avro4k.serializer.BigDecimalSerializer import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import kotlinx.serialization.Serializable -import org.apache.avro.AvroTypeException import java.math.BigDecimal import kotlin.io.path.Path @@ -60,8 +59,8 @@ internal class AvroDefaultSchemaTest : FunSpec({ } test("schema for data class with @AvroDefault should throw error when array type does not match default value type") { - shouldThrow { Avro.schema(BarInvalidArrayType.serializer()) } - shouldThrow { Avro.schema(BarInvalidNonArrayType.serializer()) } + shouldThrow { Avro.schema(BarInvalidArrayType.serializer()) } + shouldThrow { Avro.schema(BarInvalidNonArrayType.serializer()) } } }) { @Serializable diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDocSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroDocSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDocSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroDocSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroGeneratedTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroGeneratedTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroGeneratedTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroGeneratedTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroNameTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroNameTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroNameTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroNameTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroPropsSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroPropsSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroPropsSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/AvroPropsSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BasicSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BasicSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BasicSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BasicSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigIntegerSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BigIntegerSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigIntegerSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BigIntegerSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BytesSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BytesSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BytesSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/BytesSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ContextualSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/ContextualSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ContextualSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/ContextualSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/DateSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/DateSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/DateSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/DateSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/FieldNamingStrategySchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/FieldNamingStrategySchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/FieldNamingStrategySchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/FieldNamingStrategySchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/KotlinUuidSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/KotlinUuidSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/KotlinUuidSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/KotlinUuidSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/PrimitiveSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/PrimitiveSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/PrimitiveSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/PrimitiveSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/RecursiveSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/RecursiveSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/RecursiveSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/RecursiveSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/TransientSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/TransientSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/TransientSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/TransientSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/URLSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/URLSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/URLSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/URLSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/UUIDSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/UUIDSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/UUIDSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/UUIDSchemaTest.kt diff --git a/core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/UnionSchemaTest.kt b/core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/UnionSchemaTest.kt similarity index 100% rename from core/src/test/kotlin/com/github/avrokotlin/avro4k/schema/UnionSchemaTest.kt rename to core/src/jvmTest/kotlin/com/github/avrokotlin/avro4k/schema/UnionSchemaTest.kt diff --git a/core/src/test/resources/aliases_on_fields.json b/core/src/jvmTest/resources/aliases_on_fields.json similarity index 100% rename from core/src/test/resources/aliases_on_fields.json rename to core/src/jvmTest/resources/aliases_on_fields.json diff --git a/core/src/test/resources/aliases_on_fields_multiple.json b/core/src/jvmTest/resources/aliases_on_fields_multiple.json similarity index 100% rename from core/src/test/resources/aliases_on_fields_multiple.json rename to core/src/jvmTest/resources/aliases_on_fields_multiple.json diff --git a/core/src/test/resources/aliases_on_types.json b/core/src/jvmTest/resources/aliases_on_types.json similarity index 100% rename from core/src/test/resources/aliases_on_types.json rename to core/src/jvmTest/resources/aliases_on_types.json diff --git a/core/src/test/resources/aliases_on_types_multiple.json b/core/src/jvmTest/resources/aliases_on_types_multiple.json similarity index 100% rename from core/src/test/resources/aliases_on_types_multiple.json rename to core/src/jvmTest/resources/aliases_on_types_multiple.json diff --git a/core/src/test/resources/array.json b/core/src/jvmTest/resources/array.json similarity index 100% rename from core/src/test/resources/array.json rename to core/src/jvmTest/resources/array.json diff --git a/core/src/test/resources/array_of_maps.json b/core/src/jvmTest/resources/array_of_maps.json similarity index 100% rename from core/src/test/resources/array_of_maps.json rename to core/src/jvmTest/resources/array_of_maps.json diff --git a/core/src/test/resources/arrayrecords.json b/core/src/jvmTest/resources/arrayrecords.json similarity index 100% rename from core/src/test/resources/arrayrecords.json rename to core/src/jvmTest/resources/arrayrecords.json diff --git a/core/src/test/resources/avro_default_annotation_array.json b/core/src/jvmTest/resources/avro_default_annotation_array.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_array.json rename to core/src/jvmTest/resources/avro_default_annotation_array.json diff --git a/core/src/test/resources/avro_default_annotation_big_decimal.json b/core/src/jvmTest/resources/avro_default_annotation_big_decimal.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_big_decimal.json rename to core/src/jvmTest/resources/avro_default_annotation_big_decimal.json diff --git a/core/src/test/resources/avro_default_annotation_enum.json b/core/src/jvmTest/resources/avro_default_annotation_enum.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_enum.json rename to core/src/jvmTest/resources/avro_default_annotation_enum.json diff --git a/core/src/test/resources/avro_default_annotation_float.json b/core/src/jvmTest/resources/avro_default_annotation_float.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_float.json rename to core/src/jvmTest/resources/avro_default_annotation_float.json diff --git a/core/src/test/resources/avro_default_annotation_int.json b/core/src/jvmTest/resources/avro_default_annotation_int.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_int.json rename to core/src/jvmTest/resources/avro_default_annotation_int.json diff --git a/core/src/test/resources/avro_default_annotation_list.json b/core/src/jvmTest/resources/avro_default_annotation_list.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_list.json rename to core/src/jvmTest/resources/avro_default_annotation_list.json diff --git a/core/src/test/resources/avro_default_annotation_list_of_records.json b/core/src/jvmTest/resources/avro_default_annotation_list_of_records.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_list_of_records.json rename to core/src/jvmTest/resources/avro_default_annotation_list_of_records.json diff --git a/core/src/test/resources/avro_default_annotation_set.json b/core/src/jvmTest/resources/avro_default_annotation_set.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_set.json rename to core/src/jvmTest/resources/avro_default_annotation_set.json diff --git a/core/src/test/resources/avro_default_annotation_string.json b/core/src/jvmTest/resources/avro_default_annotation_string.json similarity index 100% rename from core/src/test/resources/avro_default_annotation_string.json rename to core/src/jvmTest/resources/avro_default_annotation_string.json diff --git a/core/src/test/resources/avro_name_field.json b/core/src/jvmTest/resources/avro_name_field.json similarity index 100% rename from core/src/test/resources/avro_name_field.json rename to core/src/jvmTest/resources/avro_name_field.json diff --git a/core/src/test/resources/basic.json b/core/src/jvmTest/resources/basic.json similarity index 100% rename from core/src/test/resources/basic.json rename to core/src/jvmTest/resources/basic.json diff --git a/core/src/test/resources/class_of_list_of_maps.json b/core/src/jvmTest/resources/class_of_list_of_maps.json similarity index 100% rename from core/src/test/resources/class_of_list_of_maps.json rename to core/src/jvmTest/resources/class_of_list_of_maps.json diff --git a/core/src/test/resources/contextual_1.json b/core/src/jvmTest/resources/contextual_1.json similarity index 100% rename from core/src/test/resources/contextual_1.json rename to core/src/jvmTest/resources/contextual_1.json diff --git a/core/src/test/resources/deepnested.json b/core/src/jvmTest/resources/deepnested.json similarity index 100% rename from core/src/test/resources/deepnested.json rename to core/src/jvmTest/resources/deepnested.json diff --git a/core/src/test/resources/doc_annotation_class.json b/core/src/jvmTest/resources/doc_annotation_class.json similarity index 100% rename from core/src/test/resources/doc_annotation_class.json rename to core/src/jvmTest/resources/doc_annotation_class.json diff --git a/core/src/test/resources/doc_annotation_field.json b/core/src/jvmTest/resources/doc_annotation_field.json similarity index 100% rename from core/src/test/resources/doc_annotation_field.json rename to core/src/jvmTest/resources/doc_annotation_field.json diff --git a/core/src/test/resources/doc_annotation_field_struct.json b/core/src/jvmTest/resources/doc_annotation_field_struct.json similarity index 100% rename from core/src/test/resources/doc_annotation_field_struct.json rename to core/src/jvmTest/resources/doc_annotation_field_struct.json diff --git a/core/src/test/resources/enum_with_default.json b/core/src/jvmTest/resources/enum_with_default.json similarity index 100% rename from core/src/test/resources/enum_with_default.json rename to core/src/jvmTest/resources/enum_with_default.json diff --git a/core/src/test/resources/enum_with_default_record.json b/core/src/jvmTest/resources/enum_with_default_record.json similarity index 100% rename from core/src/test/resources/enum_with_default_record.json rename to core/src/jvmTest/resources/enum_with_default_record.json diff --git a/core/src/test/resources/fixed_string.json b/core/src/jvmTest/resources/fixed_string.json similarity index 100% rename from core/src/test/resources/fixed_string.json rename to core/src/jvmTest/resources/fixed_string.json diff --git a/core/src/test/resources/fixed_string_5.json b/core/src/jvmTest/resources/fixed_string_5.json similarity index 100% rename from core/src/test/resources/fixed_string_5.json rename to core/src/jvmTest/resources/fixed_string_5.json diff --git a/core/src/test/resources/list.json b/core/src/jvmTest/resources/list.json similarity index 100% rename from core/src/test/resources/list.json rename to core/src/jvmTest/resources/list.json diff --git a/core/src/test/resources/list_of_maps.json b/core/src/jvmTest/resources/list_of_maps.json similarity index 100% rename from core/src/test/resources/list_of_maps.json rename to core/src/jvmTest/resources/list_of_maps.json diff --git a/core/src/test/resources/listrecords.json b/core/src/jvmTest/resources/listrecords.json similarity index 100% rename from core/src/test/resources/listrecords.json rename to core/src/jvmTest/resources/listrecords.json diff --git a/core/src/test/resources/map_boolean_null.json b/core/src/jvmTest/resources/map_boolean_null.json similarity index 100% rename from core/src/test/resources/map_boolean_null.json rename to core/src/jvmTest/resources/map_boolean_null.json diff --git a/core/src/test/resources/map_int.json b/core/src/jvmTest/resources/map_int.json similarity index 100% rename from core/src/test/resources/map_int.json rename to core/src/jvmTest/resources/map_int.json diff --git a/core/src/test/resources/map_record.json b/core/src/jvmTest/resources/map_record.json similarity index 100% rename from core/src/test/resources/map_record.json rename to core/src/jvmTest/resources/map_record.json diff --git a/core/src/test/resources/map_set_nested.json b/core/src/jvmTest/resources/map_set_nested.json similarity index 100% rename from core/src/test/resources/map_set_nested.json rename to core/src/jvmTest/resources/map_set_nested.json diff --git a/core/src/test/resources/nested.json b/core/src/jvmTest/resources/nested.json similarity index 100% rename from core/src/test/resources/nested.json rename to core/src/jvmTest/resources/nested.json diff --git a/core/src/test/resources/nested_multiple.json b/core/src/jvmTest/resources/nested_multiple.json similarity index 100% rename from core/src/test/resources/nested_multiple.json rename to core/src/jvmTest/resources/nested_multiple.json diff --git a/core/src/test/resources/pascal_case_schema.json b/core/src/jvmTest/resources/pascal_case_schema.json similarity index 100% rename from core/src/test/resources/pascal_case_schema.json rename to core/src/jvmTest/resources/pascal_case_schema.json diff --git a/core/src/test/resources/props_json_annotation_class.json b/core/src/jvmTest/resources/props_json_annotation_class.json similarity index 100% rename from core/src/test/resources/props_json_annotation_class.json rename to core/src/jvmTest/resources/props_json_annotation_class.json diff --git a/core/src/test/resources/recursive_class.json b/core/src/jvmTest/resources/recursive_class.json similarity index 100% rename from core/src/test/resources/recursive_class.json rename to core/src/jvmTest/resources/recursive_class.json diff --git a/core/src/test/resources/recursive_list.json b/core/src/jvmTest/resources/recursive_list.json similarity index 100% rename from core/src/test/resources/recursive_list.json rename to core/src/jvmTest/resources/recursive_list.json diff --git a/core/src/test/resources/recursive_map.json b/core/src/jvmTest/resources/recursive_map.json similarity index 100% rename from core/src/test/resources/recursive_map.json rename to core/src/jvmTest/resources/recursive_map.json diff --git a/core/src/test/resources/recursive_nested.json b/core/src/jvmTest/resources/recursive_nested.json similarity index 100% rename from core/src/test/resources/recursive_nested.json rename to core/src/jvmTest/resources/recursive_nested.json diff --git a/core/src/test/resources/sealed.json b/core/src/jvmTest/resources/sealed.json similarity index 100% rename from core/src/test/resources/sealed.json rename to core/src/jvmTest/resources/sealed.json diff --git a/core/src/test/resources/sealed_nullable_referenced.json b/core/src/jvmTest/resources/sealed_nullable_referenced.json similarity index 100% rename from core/src/test/resources/sealed_nullable_referenced.json rename to core/src/jvmTest/resources/sealed_nullable_referenced.json diff --git a/core/src/test/resources/sealed_referenced.json b/core/src/jvmTest/resources/sealed_referenced.json similarity index 100% rename from core/src/test/resources/sealed_referenced.json rename to core/src/jvmTest/resources/sealed_referenced.json diff --git a/core/src/test/resources/set_of_maps.json b/core/src/jvmTest/resources/set_of_maps.json similarity index 100% rename from core/src/test/resources/set_of_maps.json rename to core/src/jvmTest/resources/set_of_maps.json diff --git a/core/src/test/resources/setdoubles.json b/core/src/jvmTest/resources/setdoubles.json similarity index 100% rename from core/src/test/resources/setdoubles.json rename to core/src/jvmTest/resources/setdoubles.json diff --git a/core/src/test/resources/setrecords.json b/core/src/jvmTest/resources/setrecords.json similarity index 100% rename from core/src/test/resources/setrecords.json rename to core/src/jvmTest/resources/setrecords.json diff --git a/core/src/test/resources/setstrings.json b/core/src/jvmTest/resources/setstrings.json similarity index 100% rename from core/src/test/resources/setstrings.json rename to core/src/jvmTest/resources/setstrings.json diff --git a/core/src/test/resources/snake_case_schema.json b/core/src/jvmTest/resources/snake_case_schema.json similarity index 100% rename from core/src/test/resources/snake_case_schema.json rename to core/src/jvmTest/resources/snake_case_schema.json diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt b/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt deleted file mode 100644 index b8bcb6e1..00000000 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt +++ /dev/null @@ -1,216 +0,0 @@ -package com.github.avrokotlin.avro4k.internal.schema - -import com.github.avrokotlin.avro4k.AvroDefault -import com.github.avrokotlin.avro4k.internal.asSchemaList -import com.github.avrokotlin.avro4k.internal.isStartingAsJson -import com.github.avrokotlin.avro4k.internal.jsonNode -import com.github.avrokotlin.avro4k.internal.nonNullSerialName -import com.github.avrokotlin.avro4k.serializer.ElementLocation -import kotlinx.serialization.SerializationException -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.boolean -import kotlinx.serialization.json.booleanOrNull -import org.apache.avro.JsonProperties -import org.apache.avro.Schema - -internal class ClassVisitor( - descriptor: SerialDescriptor, - private val context: VisitorContext, - private val onSchemaBuilt: (Schema) -> Unit, -) : SerialDescriptorClassVisitor { - private val fields = mutableListOf() - private val schemaAlreadyResolved: Boolean - private val schema: Schema - - init { - var schemaAlreadyResolved = true - schema = - context.resolvedSchemas.getOrPut(descriptor.nonNullSerialName) { - schemaAlreadyResolved = false - - val annotations = TypeAnnotations(descriptor) - val schema = - Schema.createRecord( - // name = - descriptor.nonNullSerialName, - // doc = - annotations.doc?.value, - // namespace = - null, - // isError = - false - ) - annotations.aliases?.value?.forEach { schema.addAlias(it) } - annotations.props.forEach { schema.addProp(it.key, it.jsonNode) } - schema - } - this.schemaAlreadyResolved = schemaAlreadyResolved - } - - override fun visitClassElement( - descriptor: SerialDescriptor, - elementIndex: Int, - ): SerialDescriptorValueVisitor? { - if (schemaAlreadyResolved) { - return null - } - return ValueVisitor(context.copy(inlinedElements = listOf(ElementLocation(descriptor, elementIndex)))) { fieldSchema -> - fields.add( - createField( - context.avro.configuration.fieldNamingStrategy.resolve(descriptor, elementIndex), - FieldAnnotations(descriptor, elementIndex), - fieldSchema - ) - ) - } - } - - override fun endClassVisit(descriptor: SerialDescriptor) { - if (!schemaAlreadyResolved) { - schema.fields = fields - } - onSchemaBuilt(schema) - } - - /** - * Create a field with the given annotations. - * Here are managed the generic field level annotations: - * - namespaceOverride - * - default (also sort unions according to the default value) - * - aliases - * - doc - * - props & json props - */ - private fun createField( - fieldName: String, - annotations: FieldAnnotations, - elementSchema: Schema, - ): Schema.Field { - val (finalSchema, fieldDefault) = getDefaultAndReorderUnionIfNeeded(annotations, elementSchema) - - val field = - Schema.Field( - fieldName, - finalSchema, - annotations.doc?.value, - fieldDefault - ) - annotations.aliases?.value?.forEach { field.addAlias(it) } - annotations.props.forEach { field.addProp(it.key, it.jsonNode) } - return field - } - - private fun getDefaultAndReorderUnionIfNeeded( - annotations: FieldAnnotations, - elementSchema: Schema, - ): Pair { - val defaultValue = annotations.default?.toAvroObject() - if (defaultValue == null) { - if (context.configuration.implicitNulls && elementSchema.isNullable) { - return elementSchema.moveToHeadOfUnion { it.type == Schema.Type.NULL } to JsonProperties.NULL_VALUE - } else if (context.configuration.implicitEmptyCollections) { - elementSchema.asSchemaList().forEachIndexed { index, schema -> - if (schema.type == Schema.Type.ARRAY) { - return elementSchema.moveToHeadOfUnion(index) to emptyList() - } - if (schema.type == Schema.Type.MAP) { - return elementSchema.moveToHeadOfUnion(index) to emptyMap() - } - } - } - } else if (defaultValue === JsonProperties.NULL_VALUE) { - // If the user sets "null" but the field is not nullable, maybe the user wanted to set the "null" string default - val finalSchema = elementSchema.moveToHeadOfUnion { it.type == Schema.Type.NULL } - val adaptedDefault = if (!elementSchema.isNullable) "null" else defaultValue - return finalSchema to adaptedDefault - } else if (elementSchema.asSchemaList().any { it.logicalType?.name == CHAR_LOGICAL_TYPE_NAME }) { - // requires a string default value with exactly 1 character, and map the character to the char code as it is an int - if (defaultValue is String && defaultValue.length == 1) { - return elementSchema.moveToHeadOfUnion { it.logicalType?.name == CHAR_LOGICAL_TYPE_NAME } to defaultValue.single().code - } else { - throw SerializationException("Default value for Char must be a single character string. Invalid value of type ${defaultValue::class.qualifiedName}: $defaultValue") - } - } else if (elementSchema.isNullable) { - // default is not null, so let's just put the null schema at the end of the union which should cover the main use cases - return elementSchema.moveToTailOfUnion { it.type === Schema.Type.NULL } to defaultValue - } - return elementSchema to defaultValue - } -} - -private fun AvroDefault.toAvroObject(): Any { - if (value.isStartingAsJson()) { - return Json.parseToJsonElement(value).toAvroObject() - } - return value -} - -private fun JsonElement.toAvroObject(): Any = - when (this) { - is JsonNull -> JsonProperties.NULL_VALUE - - is JsonObject -> this.entries.associate { it.key to it.value.toAvroObject() } - - is JsonArray -> this.map { it.toAvroObject() } - - is JsonPrimitive -> - when { - this.isString -> this.content - - this.booleanOrNull != null -> this.boolean - - else -> { - this.content.toBigDecimal().stripTrailingZeros().let { - if (it.scale() <= 0) { - it.toBigInteger() - } else { - it - } - } - } - } - } - -private fun Schema.moveToHeadOfUnion(predicate: (Schema) -> Boolean): Schema { - if (!isUnion) { - return this - } - types.indexOfFirst(predicate).let { index -> - if (index == -1) { - return this - } - return moveToHeadOfUnion(index) - } -} - -private fun Schema.moveToHeadOfUnion(typeIndex: Int): Schema { - if (!isUnion || typeIndex >= types.size) { - return this - } - return Schema.createUnion(types.toMutableList().apply { add(0, removeAt(typeIndex)) }) -} - -private fun Schema.moveToTailOfUnion(predicate: (Schema) -> Boolean): Schema { - if (!isUnion) { - return this - } - types.indexOfFirst(predicate).let { index -> - if (index == -1) { - return this - } - return moveToTailOfUnion(index) - } -} - -private fun Schema.moveToTailOfUnion(typeIndex: Int): Schema { - if (!isUnion || typeIndex >= types.size) { - return this - } - return Schema.createUnion(types.toMutableList().apply { add(removeAt(typeIndex)) }) -} \ No newline at end of file diff --git a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/InlineClassVisitor.kt b/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/InlineClassVisitor.kt deleted file mode 100644 index d3bac92d..00000000 --- a/core/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/InlineClassVisitor.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.github.avrokotlin.avro4k.internal.schema - -import com.github.avrokotlin.avro4k.internal.copy -import com.github.avrokotlin.avro4k.internal.isNamedSchema -import com.github.avrokotlin.avro4k.internal.jsonNode -import com.github.avrokotlin.avro4k.serializer.AvroSerializer -import com.github.avrokotlin.avro4k.serializer.ElementLocation -import kotlinx.serialization.SerializationException -import kotlinx.serialization.descriptors.SerialDescriptor -import org.apache.avro.Schema - -internal class InlineClassVisitor( - private val context: VisitorContext, - private val onSchemaBuilt: (Schema) -> Unit, -) : SerialDescriptorInlineClassVisitor { - override fun visitInlineClassElement( - inlineClassDescriptor: SerialDescriptor, - inlineElementIndex: Int, - ): SerialDescriptorValueVisitor { - val inlinedElements = context.inlinedElements + ElementLocation(inlineClassDescriptor, inlineElementIndex) - return ValueVisitor(context.copy(inlinedElements = inlinedElements)) { generatedSchema -> - val annotations = InlineClassFieldAnnotations(inlineClassDescriptor) - - val props = annotations.props.toList() - val schema = - if (props.isNotEmpty()) { - if (generatedSchema.isNamedSchema()) { - throw SerializationException( - "The value class property '${inlineClassDescriptor.serialName}.${inlineClassDescriptor.getElementName(0)}' has " + - "forbidden additional properties $props for the named schema ${generatedSchema.fullName}. " + - "Please create your own serializer extending ${AvroSerializer::class.qualifiedName} to add properties to a named schema." - ) - } - generatedSchema.copy(additionalProps = props.associate { it.key to it.jsonNode }) - } else { - generatedSchema - } - - onSchemaBuilt(schema) - } - } -} \ No newline at end of file diff --git a/core/src/nativeMain/kotlin/internal/IdentitySet.native.kt b/core/src/nativeMain/kotlin/internal/IdentitySet.native.kt new file mode 100644 index 00000000..5d1bc8b8 --- /dev/null +++ b/core/src/nativeMain/kotlin/internal/IdentitySet.native.kt @@ -0,0 +1,26 @@ +package com.github.avrokotlin.avro4k.internal + +import kotlin.experimental.ExperimentalNativeApi +import kotlin.native.identityHashCode + +internal actual class IdentitySet { + private val backingSet: MutableSet = mutableSetOf() + + actual fun add(value: T): Boolean { + return backingSet.add(Item(value)) + } + + actual fun remove(value: T): Boolean { + return backingSet.remove(Item(value)) + } + + private inner class Item(val value: T) { + @OptIn(ExperimentalNativeApi::class) + override fun hashCode(): Int = value.identityHashCode() + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is IdentitySet<*>.Item) return false + return value === other.value + } + } +} \ No newline at end of file diff --git a/core/src/nativeMain/kotlin/internal/WeakIdentityKeyCache.native.kt b/core/src/nativeMain/kotlin/internal/WeakIdentityKeyCache.native.kt new file mode 100644 index 00000000..c1c51b8c --- /dev/null +++ b/core/src/nativeMain/kotlin/internal/WeakIdentityKeyCache.native.kt @@ -0,0 +1,74 @@ +package com.github.avrokotlin.avro4k.internal + +import com.github.avrokotlin.avro4k.InternalAvro4kApi +import kotlin.experimental.ExperimentalNativeApi +import kotlin.native.identityHashCode +import kotlin.native.ref.WeakReference + +@InternalAvro4kApi +@OptIn(ExperimentalNativeApi::class) +public actual class WeakIdentityKeyCache { + private val map = mutableMapOf, V>() + + internal val size: Int get() = map.size + internal operator fun contains(key: K): Boolean = map.containsKey(Key.Lookup(key.identityHashCode(), key)) + + @InternalAvro4kApi + public actual fun getOrPut(key: K, defaultValue: () -> V): V { + // "get" phase + val hash = key.identityHashCode() + map[Key.Lookup(hash, key)]?.let { return it } + + // Cleaning phase + removeStaleEntries() + + // "put" phase + return map.getOrPut(Key.Stored(hash, key)) { defaultValue() } + } + + private fun removeStaleEntries() { + map.keys.removeAll { (it as Key.Stored<*>).refence.value == null } + } + + private sealed interface Key { + val value: K? + class Stored(val hash: Int, value: K) : Key { + val refence = WeakReference(value) + + override val value: K? get() = refence.value + + override fun hashCode(): Int = hash + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Key<*>) return false + + val a = value + val b = other.value + // Stale (null) entries are being removed, so they should not match + if (a == null || b == null) return false + + return a === b + } + } + + /** + * Strong reference to the key to ensure the key isn't garbage collected during lookup and to + * prevent a WeakReference to be created and referenced for potential queuing just for lookup. + */ + class Lookup(val hash: Int, override val value: K) : Key { + override fun hashCode(): Int = hash + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Key<*>) return false + + val a = value + // Stale (null) entries are being removed, so they should not match + val b = other.value ?: return false + + return a === b + } + } + } +} \ No newline at end of file diff --git a/core/src/nativeTest/kotlin/internal/WeakKeyCacheTest.kt b/core/src/nativeTest/kotlin/internal/WeakKeyCacheTest.kt new file mode 100644 index 00000000..85e1bc8e --- /dev/null +++ b/core/src/nativeTest/kotlin/internal/WeakKeyCacheTest.kt @@ -0,0 +1,98 @@ +package com.github.avrokotlin.avro4k.internal + +import kotlin.native.runtime.GC +import kotlin.native.runtime.NativeRuntimeApi +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotSame +import kotlin.test.assertSame +import kotlin.test.assertTrue + +@OptIn(NativeRuntimeApi::class) +class WeakKeyCacheTest { + private data class Key(val id: Int) + + @Test + fun `entry is returned from cache when key is still referenced`() { + val cache = WeakIdentityKeyCache() + var computeCount = 0 + + val key = Key(1) + val firstValue = Any() + assertSame(firstValue, cache.getOrPut(key) { ++computeCount; firstValue }) + // Same reference, so reusing the first value + assertSame(firstValue, cache.getOrPut(key) { ++computeCount; Any() }) + + GC.collect() + + // Same reference, even after GC + assertSame(firstValue, cache.getOrPut(key) { ++computeCount; Any() }) + // Value getter only gets executed once + assertEquals(1, computeCount) + // Only one unique entry in the cache + assertEquals(1, cache.size) + assertTrue(key in cache) + } + + @Test + fun `stale entry is recalculated when getOrPut is called after GC on the same key`() { + val cache = WeakIdentityKeyCache() + + fun addWeakEntry(id: Int): Any { + val key = Key(id) + return cache.getOrPut(key) { Any() } + // key goes out of scope here + } + + val firstValue = addWeakEntry(1) + + // Should not be the same reference, as the key is equal but not identical instance + assertNotSame(firstValue, addWeakEntry(1)) + + GC.collect() + + // The cache still contains the entry, even if it is no longer accessible, + // thus waiting for the next put to trigger the eviction + assertEquals(2, cache.size) + // Should be a new reference, as the key is no longer accessible + assertNotSame(firstValue, addWeakEntry(1)) + // Stale entries should be cleaned after a put + assertEquals(1, cache.size) + } + + @Test + fun `stale entries are not cleaned when getOrPut hits the cache`() { + val cache = WeakIdentityKeyCache() + + fun addWeakEntry(id: Int): Any { + val key = Key(id) + return cache.getOrPut(key) { Any() } + // key goes out of scope here + } + + val liveKey = Key(0) + val valueFromLiveKey = cache.getOrPut(liveKey) { Any() } + + val value1FromWeakKey = addWeakEntry(1) + val value2FromWeakKey = addWeakEntry(2) + + GC.collect() + // The cache still contains the stale entries, even if it is no longer accessible, + // thus waiting for the next put to trigger the eviction + assertEquals(3, cache.size) + assertTrue(liveKey in cache) + + // Cache hit on liveKey → no cleanup triggered, stale entries remain + assertSame(valueFromLiveKey, cache.getOrPut(liveKey) { Any() }) + assertEquals(3, cache.size) + + // Now, any unknown key will first remove the stale entries, then store the new value + addWeakEntry(1000) + // Size now 2 as the Key(1) and Key(2) has been evicted while Key(0) remains and Key(1000) added + assertEquals(2, cache.size) + + // As stale entries have been cleaned up during the Key(1000) miss + assertNotSame(value1FromWeakKey, addWeakEntry(1)) + assertNotSame(value2FromWeakKey, addWeakEntry(2)) + } +} diff --git a/gradle.properties b/gradle.properties index d43524de..295ae5b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,13 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true +kotlin.incremental.native=true +org.gradle.jvmargs=-Xmx2g # Specify version for local builds. Should be overridden during release. version=local-SNAPSHOT # enable caching org.gradle.caching=true -org.gradle.configuration-cache=true \ No newline at end of file +org.gradle.configuration-cache=true +# disable caching of native binaries because of an issue during the build +kotlin.native.cacheKind=none \ No newline at end of file diff --git a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AnnotationsHelper.kt b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AnnotationsHelper.kt index 7a1dcccb..9f25d651 100644 --- a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AnnotationsHelper.kt +++ b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AnnotationsHelper.kt @@ -1,5 +1,11 @@ package com.github.avrokotlin.avro4k +import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema +import com.github.avrokotlin.avro4k.AvroSchema.BytesSchema +import com.github.avrokotlin.avro4k.AvroSchema.FixedSchema +import com.github.avrokotlin.avro4k.AvroSchema.MapSchema +import com.github.avrokotlin.avro4k.AvroSchema.NamedSchema +import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema import com.github.avrokotlin.avro4k.internal.AvroGenerated import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.ClassName @@ -13,7 +19,7 @@ import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonPrimitive internal fun buildAvroFixedAnnotation(schema: AvroSchema): AnnotationSpec? { - if (schema !is AvroSchema.FixedSchema) { + if (schema !is FixedSchema) { return null } return AnnotationSpec.builder(AvroFixed::class.asClassName()) @@ -33,7 +39,7 @@ internal fun buildAvroPropAnnotations(carrier: WithProps): List } } -internal fun buildAvroAliasAnnotation(carrier: AvroSchema.NamedSchema): AnnotationSpec? { +internal fun buildAvroAliasAnnotation(carrier: NamedSchema): AnnotationSpec? { return buildAvroAliasAnnotation(carrier.aliases.map { it.fullName }) } @@ -51,7 +57,7 @@ internal fun buildAvroAliasAnnotation(aliases: Collection): AnnotationSp } internal fun buildAvroDecimalAnnotation(schema: AvroSchema): AnnotationSpec? { - if ((schema is AvroSchema.FixedSchema || schema is AvroSchema.BytesSchema) && schema.logicalTypeName == "decimal") { + if ((schema is FixedSchema || schema is BytesSchema) && schema.logicalTypeName == "decimal") { val scale = schema.props["scale"]?.jsonPrimitive?.intOrNull val precision = schema.props["precision"]?.jsonPrimitive?.intOrNull precision ?: error("Missing 'precision' prop for 'decimal' logical type of schema $schema") @@ -70,12 +76,12 @@ internal fun buildAvroGeneratedAnnotation(schema: AvroSchema): AnnotationSpec { .build() } -internal fun buildAvroDefaultAnnotation(field: AvroSchema.RecordSchema.Field): AnnotationSpec? { +internal fun buildAvroDefaultAnnotation(field: RecordSchema.Field): AnnotationSpec? { if (field.defaultValue == null) { return null } return AnnotationSpec.builder(AvroDefault::class.asClassName()) - .addMember("%S", field.defaultValue.contentUnquoted) + .addMember("%S", field.defaultValue!!.contentUnquoted) .build() } @@ -83,9 +89,9 @@ internal fun buildImplicitAvroDefaultCodeBlock(schema: AvroSchema, implicitNulls if (implicitNulls && schema.isNullable) { return CodeBlock.of("null") } else if (implicitEmptyCollections) { - if (schema is AvroSchema.ArraySchema) { + if (schema is ArraySchema) { return getListOfCodeBlock(emptyList()) - } else if (schema is AvroSchema.MapSchema) { + } else if (schema is MapSchema) { return getMapOfCodeBlock(emptyMap()) } } diff --git a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchema.kt b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchema.kt deleted file mode 100644 index e1dbb5f2..00000000 --- a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchema.kt +++ /dev/null @@ -1,281 +0,0 @@ -package com.github.avrokotlin.avro4k - -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.contentOrNull -import kotlinx.serialization.json.jsonPrimitive -import org.apache.avro.specific.SpecificData - -internal interface WithProps { - val props: Map -} - -internal interface WithDoc { - val doc: String? -} - -internal val AvroSchema.isNullable: Boolean get() = this is AvroSchema.NullSchema || (this is AvroSchema.UnionSchema && isNullable) - -internal val AvroSchema.actualJavaClassName: String? - get() = (this as? WithProps)?.props[SpecificData.CLASS_PROP]?.jsonPrimitive?.contentOrNull - -internal val AvroSchema.logicalTypeName: String? - get() = (this as? WithProps)?.props["logicalType"]?.jsonPrimitive?.contentOrNull - -internal val AvroSchema.ArraySchema.actualElementClass: String? - get() = props[SpecificData.ELEMENT_PROP]?.jsonPrimitive?.contentOrNull - -internal val AvroSchema.MapSchema.actualKeyClass: String? - get() = props[SpecificData.KEY_CLASS_PROP]?.jsonPrimitive?.contentOrNull - -/** - * This class is the future drop-in multiplatform replacement for the Apache Avro Schema class. - * It is not in the core module as it is tested first here. Then it will be moved to core when ready. - */ -internal sealed interface AvroSchema { - val fullName: String get() = simpleName - val simpleName: String - - sealed interface PrimitiveSchema : AvroSchema, WithProps - - data class BooleanSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "boolean" - } - - data class IntSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "int" - } - - data class LongSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "long" - } - - data class FloatSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "float" - } - - data class DoubleSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "double" - } - - data class BytesSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "bytes" - } - - data class StringSchema( - override val props: Map = emptyMap(), - ) : PrimitiveSchema { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "string" - } - - data class NullSchema( - override val props: Map = emptyMap(), - ) : AvroSchema, WithProps { - init { - ensureNotProhibitedProp("type") - } - - override val simpleName: String - get() = "null" - } - - data class UnionSchema( - val types: List, - ) : AvroSchema { - val isNullable: Boolean = types.any { it is NullSchema } - val isSimpleNullableType: Boolean get() = types.size == 2 && isNullable - val typesByFullName = types.associateBy { it.fullName } - - init { - require(types.isNotEmpty()) { "Union must have at least one type" } - require(typesByFullName.size == types.size) { "Union cannot contain duplicate types" } - } - - override val simpleName: String - get() = "union" - } - - data class ArraySchema( - val elementSchema: AvroSchema, - override val props: Map = emptyMap(), - ) : AvroSchema, WithProps { - init { - ensureNotProhibitedProp("type", "items") - } - - override val simpleName: String - get() = "array" - } - - data class MapSchema( - val valueSchema: AvroSchema, - override val props: Map = emptyMap(), - ) : AvroSchema, WithProps { - init { - ensureNotProhibitedProp("type", "values") - } - - override val simpleName: String - get() = "map" - } - - sealed interface NamedSchema : AvroSchema, WithProps, WithDoc { - val name: Name - val aliases: Set - - override val fullName: String - get() = name.fullName - override val simpleName: String - get() = name.simpleName - } - - data class RecordSchema( - override val name: Name, - val fields: List, - override val doc: String? = null, - override val aliases: Set = emptySet(), - override val props: Map = emptyMap(), - ) : NamedSchema { - // set as lazy as records are created first with an empty fields list, then only the list is populated, so this map has to be calculated after it is populated - private val fieldsByName by lazy { fields.associateBy { it.name } } - - init { - ensureNotProhibitedProp("type", "fields", "name", "namespace", "aliases", "doc") - } - - fun getFieldByName(fieldName: String): Field { - return fieldsByName.getValue(fieldName) - } - - data class Field( - val name: String, - val schema: AvroSchema, - val defaultValue: JsonElement? = null, - override val doc: String? = null, - val aliases: Set = emptySet(), - override val props: Map = emptyMap(), - ) : WithProps, WithDoc { - init { - require(name !in aliases) { "Field name '$name' cannot be part of aliases $aliases" } - ensureNotProhibitedProp("name", "type", "aliases", "doc", "default") - } - } - } - - data class FixedSchema( - override val name: Name, - val size: UInt, - override val doc: String? = null, - override val aliases: Set = emptySet(), - override val props: Map = emptyMap(), - ) : NamedSchema { - init { - ensureNotProhibitedProp("type", "size", "name", "namespace", "aliases", "doc") - } - } - - data class EnumSchema( - override val name: Name, - val symbols: Set, - val defaultSymbol: String? = null, - override val doc: String? = null, - override val aliases: Set = emptySet(), - override val props: Map = emptyMap(), - ) : NamedSchema { - init { - require(defaultSymbol == null || defaultSymbol in symbols) { "Default symbol must be one of the enum symbols" } - ensureNotProhibitedProp("type", "default", "symbols", "name", "namespace", "aliases", "doc") - } - } - - companion object { - // placeholder to allow static extensions to be added like AvroSchema.fromJson() - } -} - -internal class Name { - val fullName: String - val simpleName: String - val space: String? - - constructor(name: String, space: String?) { - if (space.isNullOrEmpty()) { - this.simpleName = name.substringAfterLast('.') - this.space = name.substringBeforeLast('.', missingDelimiterValue = "").takeIf { it.isNotEmpty() } - } else { - require(!name.contains('.')) { "Name cannot contain a dot if the namespace is provided: name=$name, space=$space" } - this.simpleName = name - this.space = space - } - this.fullName = if (this.space == null) this.simpleName else "${this.space}.${this.simpleName}" - } - - constructor(fullName: String) : this(fullName, null) - - internal fun withSpaceIfMissing(space: String?) = Name(name = simpleName, space = space ?: this.space) - - override fun toString(): String { - return fullName - } - - override fun equals(other: Any?): Boolean { - if (other !is Name) return false - return fullName == other.fullName - } - - override fun hashCode(): Int { - return fullName.hashCode() - } -} - -private fun WithProps.ensureNotProhibitedProp(vararg prohibited: String) { - for (p in prohibited) { - require(p !in props) { "properties in ${this::class.simpleName} must not contain the field '$p'. Actual value: ${props[p]}" } - } -} \ No newline at end of file diff --git a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchemaJsonSerialization.kt b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchemaJsonSerialization.kt deleted file mode 100644 index 9216bf41..00000000 --- a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchemaJsonSerialization.kt +++ /dev/null @@ -1,262 +0,0 @@ -@file:OptIn(ExperimentalSerializationApi::class) - -package com.github.avrokotlin.avro4k - -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonObjectBuilder -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.addAll -import kotlinx.serialization.json.addJsonObject -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.contentOrNull -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonArray - -internal fun AvroSchema.Companion.fromJsonElement(element: JsonElement, knownNamedTypes: MutableMap = mutableMapOf()): AvroSchema { - return fromJsonElement(element, null, knownNamedTypes) -} - -private fun fromJsonElement(element: JsonElement, currentSpace: String?, knownNamedTypes: MutableMap): AvroSchema { - return when (element) { - is JsonPrimitive -> { - // Handle shorthand primitive type or referenced named type - when (val type = element.contentOrNull) { - "null" -> AvroSchema.NullSchema() - - "boolean" -> AvroSchema.BooleanSchema() - - "int" -> AvroSchema.IntSchema() - - "long" -> AvroSchema.LongSchema() - - "float" -> AvroSchema.FloatSchema() - - "double" -> AvroSchema.DoubleSchema() - - "bytes" -> AvroSchema.BytesSchema() - - "string" -> AvroSchema.StringSchema() - - else -> - knownNamedTypes[type] - ?: throw IllegalArgumentException("Unknown named type: $type") - } - } - - is JsonArray -> { - // Handle union - AvroSchema.UnionSchema(element.map { fromJsonElement(it, currentSpace, knownNamedTypes) }) - } - - is JsonObject -> { - val props = element.toMutableMap() - when (val type = props.removeMandatory("type").stringPrimitive) { - "null" -> AvroSchema.NullSchema(props = props) - - "boolean" -> AvroSchema.BooleanSchema(props = props) - - "int" -> AvroSchema.IntSchema(props = props) - - "long" -> AvroSchema.LongSchema(props = props) - - "float" -> AvroSchema.FloatSchema(props = props) - - "double" -> AvroSchema.DoubleSchema(props = props) - - "bytes" -> AvroSchema.BytesSchema(props = props) - - "string" -> AvroSchema.StringSchema(props = props) - - "map" -> - AvroSchema.MapSchema( - valueSchema = fromJsonElement(props.removeMandatory("values"), currentSpace, knownNamedTypes), - props = props - ) - - "array" -> - AvroSchema.ArraySchema( - elementSchema = fromJsonElement(props.removeMandatory("items"), currentSpace, knownNamedTypes), - props = props - ) - - "enum" -> { - val name = props.removeName().withSpaceIfMissing(currentSpace) - AvroSchema.EnumSchema( - name = name, - symbols = props.removeMandatory("symbols").jsonArray.map { it.stringPrimitive }.toSet(), - defaultSymbol = props.remove("default")?.stringPrimitive, - doc = props.remove("doc")?.stringPrimitive, - aliases = props.removeAliases(name.space) ?: emptySet(), - props = props - ) - } - - "fixed" -> { - val name = props.removeName().withSpaceIfMissing(currentSpace) - AvroSchema.FixedSchema( - name = name, - size = props.removeMandatory("size").jsonPrimitive.int.toUInt(), - doc = props.remove("doc")?.stringPrimitive, - aliases = props.removeAliases(name.space) ?: emptySet(), - props = props - ) - } - - "record" -> { - val name = props.removeName().withSpaceIfMissing(currentSpace) - AvroSchema.RecordSchema( - name = name, - fields = - props.removeMandatory("fields").jsonArray.map { field -> - val fieldProps = field.jsonObject.toMutableMap() - AvroSchema.RecordSchema.Field( - name = fieldProps.removeMandatory("name").stringPrimitive, - schema = fromJsonElement(fieldProps.removeMandatory("type"), name.space, knownNamedTypes), - doc = fieldProps.remove("doc")?.stringPrimitive, - aliases = fieldProps.remove("aliases")?.jsonArray?.map { it.stringPrimitive }?.toSet() ?: emptySet(), - // TODO validate record's default, requiring all the fields to be present, and fixed default to be of expected size - defaultValue = fieldProps.remove("default"), - props = fieldProps - ) - }, - doc = props.remove("doc")?.stringPrimitive, - aliases = props.removeAliases(name.space) ?: emptySet(), - props = props - ) - } - - else -> throw IllegalArgumentException("Unknown schema type: $type") - } - } - } -} - -private fun MutableMap.removeAliases( - namespace: String?, -): Set? = remove("aliases")?.jsonArray?.map { Name(it.stringPrimitive).withSpaceIfMissing(namespace) }?.toSet() - -private fun MutableMap.removeMandatory(key: K): V { - return this.remove(key) ?: throw IllegalArgumentException("Missing '$key' key") -} - -private fun MutableMap.removeName(): Name { - val name = removeMandatory("name").stringPrimitive - val namespace = remove("namespace")?.stringPrimitive - return Name(name = name, space = namespace) -} - -private val JsonElement.stringPrimitive: String - get() { - if (this !is JsonPrimitive || isString) throw IllegalArgumentException("Not a string: $this") - return content - } - -private fun JsonObjectBuilder.putAll(map: Map) { - map.forEach { (key, value) -> put(key, value) } -} - -internal fun AvroSchema.toJsonElement(knownNamedTypes: MutableSet = mutableSetOf()): JsonElement { - return toJsonElement(null, knownNamedTypes) -} - -private fun AvroSchema.toJsonElement(currentSpace: String? = null, knownNamedTypes: MutableSet = mutableSetOf()): JsonElement { - return when (this) { - is AvroSchema.PrimitiveSchema, is AvroSchema.NullSchema -> - if (props.isEmpty()) { - JsonPrimitive(fullName) - } else { - buildJsonObject { - put("type", fullName) - putAll(props) - } - } - - is AvroSchema.UnionSchema -> JsonArray(types.map { it.toJsonElement(currentSpace, knownNamedTypes) }) - - is AvroSchema.ArraySchema -> - buildJsonObject { - put("type", "array") - put("items", elementSchema.toJsonElement(currentSpace, knownNamedTypes)) - putAll(props) - } - - is AvroSchema.MapSchema -> - buildJsonObject { - put("type", "map") - put("values", valueSchema.toJsonElement(currentSpace, knownNamedTypes)) - putAll(props) - } - - is AvroSchema.NamedSchema -> { - if (fullName in knownNamedTypes) { - // Known types are inlined, as they are already defined earlier in the schema, or are provided by the user in another file by example - JsonPrimitive(if (name.space == currentSpace) name.simpleName else fullName) - } else { - knownNamedTypes.add(fullName) - when (this) { - is AvroSchema.EnumSchema -> - buildJsonObject { - put("type", "enum") - putNamedSchemaProps(this@toJsonElement, currentSpace) - putJsonArray("symbols") { addAll(symbols) } - if (defaultSymbol != null) put("default", defaultSymbol) - putAll(props) - putAliases(aliases.map { if (name.space == it.space) it.simpleName else it.fullName }) - } - - is AvroSchema.FixedSchema -> - buildJsonObject { - put("type", "fixed") - putNamedSchemaProps(this@toJsonElement, currentSpace) - put("size", size.toInt()) - putAll(props) - putAliases(aliases.map { if (name.space == it.space) it.simpleName else it.fullName }) - } - - is AvroSchema.RecordSchema -> - buildJsonObject { - put("type", "record") - putNamedSchemaProps(this@toJsonElement, currentSpace) - putJsonArray("fields") { - fields.forEach { field -> - addJsonObject { - put("name", field.name) - put("type", field.schema.toJsonElement(name.space ?: currentSpace, knownNamedTypes)) - if (field.doc != null) put("doc", field.doc) - if (field.defaultValue != null) put("default", field.defaultValue) - putAliases(field.aliases) - putAll(field.props) - } - } - } - putAll(props) - putAliases(aliases.map { if (name.space == it.space) it.simpleName else it.fullName }) - } - } - } - } - } -} - -private fun JsonObjectBuilder.putNamedSchemaProps(schema: AvroSchema.NamedSchema, currentSpace: String?) { - put("name", schema.name.simpleName) - if (schema.name.space != null) { - if (schema.name.space != currentSpace) { - put("namespace", schema.name.space) - } - } else if (currentSpace != null) { - put("namespace", "") - } - if (schema.doc != null) put("doc", schema.doc) -} - -private fun JsonObjectBuilder.putAliases(aliases: Collection) { - if (aliases.isNotEmpty()) putJsonArray("aliases") { addAll(aliases) } -} \ No newline at end of file diff --git a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchemaJvmInterop.kt b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchemaJvmInterop.kt deleted file mode 100644 index 5402c644..00000000 --- a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/AvroSchemaJvmInterop.kt +++ /dev/null @@ -1,134 +0,0 @@ -package com.github.avrokotlin.avro4k - -import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema -import com.github.avrokotlin.avro4k.AvroSchema.BooleanSchema -import com.github.avrokotlin.avro4k.AvroSchema.BytesSchema -import com.github.avrokotlin.avro4k.AvroSchema.DoubleSchema -import com.github.avrokotlin.avro4k.AvroSchema.EnumSchema -import com.github.avrokotlin.avro4k.AvroSchema.FixedSchema -import com.github.avrokotlin.avro4k.AvroSchema.FloatSchema -import com.github.avrokotlin.avro4k.AvroSchema.IntSchema -import com.github.avrokotlin.avro4k.AvroSchema.LongSchema -import com.github.avrokotlin.avro4k.AvroSchema.MapSchema -import com.github.avrokotlin.avro4k.AvroSchema.NamedSchema -import com.github.avrokotlin.avro4k.AvroSchema.NullSchema -import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema -import com.github.avrokotlin.avro4k.AvroSchema.StringSchema -import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import org.apache.avro.JsonProperties -import org.apache.avro.Schema -import java.nio.charset.StandardCharsets - -internal fun AvroSchema.Companion.from(schema: Schema): AvroSchema { - return from(schema, mutableMapOf()) -} - -private fun from(schema: Schema, seenNamedTypes: MutableMap): AvroSchema { - seenNamedTypes[schema.fullName]?.let { - return it - } - - return when (schema.type) { - Schema.Type.RECORD -> { - val fields = mutableListOf() - val recordSchema = - RecordSchema( - name = Name(schema.name, schema.namespace), - fields = fields, - doc = schema.doc, - aliases = schema.aliasesWithSpace, - props = schema.objectProps.toJsonElementMap() - ) - seenNamedTypes[schema.fullName] = recordSchema - schema.fields.map { field -> - RecordSchema.Field( - name = field.name(), - schema = from(field.schema(), seenNamedTypes), - defaultValue = - if (field.hasDefaultValue()) { - toJsonElement(field.defaultVal()) - } else { - null - }, - doc = field.doc(), - aliases = field.aliases(), - props = field.objectProps.toJsonElementMap() - ) - }.forEach { fields += it } - recordSchema - } - - Schema.Type.ENUM -> - EnumSchema( - name = Name(schema.name, schema.namespace), - symbols = schema.enumSymbols.toSet(), - defaultSymbol = schema.enumDefault, - doc = schema.doc, - aliases = schema.aliasesWithSpace, - props = schema.objectProps.toJsonElementMap() - ).also { seenNamedTypes[schema.fullName] = it } - - Schema.Type.UNION -> UnionSchema(schema.types.map { from(it, seenNamedTypes) }) - - Schema.Type.FIXED -> - FixedSchema( - name = Name(schema.name, schema.namespace), - size = schema.fixedSize.toUInt(), - doc = schema.doc, - aliases = schema.aliasesWithSpace, - props = schema.objectProps.toJsonElementMap() - ).also { seenNamedTypes[schema.fullName] = it } - - Schema.Type.ARRAY -> - ArraySchema( - elementSchema = from(schema.elementType, seenNamedTypes), - props = schema.objectProps.toJsonElementMap() - ) - - Schema.Type.MAP -> - MapSchema( - valueSchema = from(schema.valueType, seenNamedTypes), - props = schema.objectProps.toJsonElementMap() - ) - - Schema.Type.BOOLEAN -> BooleanSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.INT -> IntSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.LONG -> LongSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.FLOAT -> FloatSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.DOUBLE -> DoubleSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.STRING -> StringSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.BYTES -> BytesSchema(schema.objectProps.toJsonElementMap()) - - Schema.Type.NULL -> NullSchema(schema.objectProps.toJsonElementMap()) - } -} - -private fun Map.toJsonElementMap(): Map { - return mapValues { (_, value) -> toJsonElement(value) } -} - -private fun toJsonElement(value: Any?): JsonElement = - when (value) { - null, JsonProperties.NULL_VALUE -> JsonNull - is Boolean -> JsonPrimitive(value) - is Number -> JsonPrimitive(value) - is CharSequence -> JsonPrimitive(value.toString()) - is ByteArray -> JsonPrimitive(String(value, StandardCharsets.ISO_8859_1)) - is List<*> -> JsonArray(value.map { toJsonElement(it) }) - is Map<*, *> -> JsonObject(value.entries.associate { it.key as String to toJsonElement(it.value) }) - else -> throw UnsupportedOperationException("unsupported value of type ${value::class}: $value") - } - -private val Schema.aliasesWithSpace: Set - get() = aliases.map { Name(it).withSpaceIfMissing(namespace) }.toSet() \ No newline at end of file diff --git a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/KotlinGenerator.kt b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/KotlinGenerator.kt index 83f3d8e1..00ece606 100644 --- a/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/KotlinGenerator.kt +++ b/kotlin-generator/src/main/kotlin/com/github/avrokotlin/avro4k/KotlinGenerator.kt @@ -1,5 +1,21 @@ package com.github.avrokotlin.avro4k +import com.github.avrokotlin.avro4k.AvroSchema.ArraySchema +import com.github.avrokotlin.avro4k.AvroSchema.BooleanSchema +import com.github.avrokotlin.avro4k.AvroSchema.BytesSchema +import com.github.avrokotlin.avro4k.AvroSchema.DoubleSchema +import com.github.avrokotlin.avro4k.AvroSchema.EnumSchema +import com.github.avrokotlin.avro4k.AvroSchema.FixedSchema +import com.github.avrokotlin.avro4k.AvroSchema.FloatSchema +import com.github.avrokotlin.avro4k.AvroSchema.IntSchema +import com.github.avrokotlin.avro4k.AvroSchema.LongSchema +import com.github.avrokotlin.avro4k.AvroSchema.MapSchema +import com.github.avrokotlin.avro4k.AvroSchema.NamedSchema +import com.github.avrokotlin.avro4k.AvroSchema.NullSchema +import com.github.avrokotlin.avro4k.AvroSchema.PrimitiveSchema +import com.github.avrokotlin.avro4k.AvroSchema.RecordSchema +import com.github.avrokotlin.avro4k.AvroSchema.StringSchema +import com.github.avrokotlin.avro4k.AvroSchema.UnionSchema import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock @@ -18,6 +34,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.boolean +import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.double import kotlinx.serialization.json.float import kotlinx.serialization.json.int @@ -97,7 +114,7 @@ public class KotlinGenerator( */ public fun generateKotlinClasses(schema: Schema, rootAnonymousSchemaName: String): List { return generateRootKotlinClasses( - AvroSchema.from(schema), + AvroSchema.fromApacheSchema(schema), potentialAnonymousClassName = rootAnonymousSchemaName.toPascalCase() ) } @@ -124,24 +141,24 @@ public class KotlinGenerator( return listOf(generateRootValueClass(schema, potentialAnonymousClassName, it).toFileSpec(null)) } return when (schema) { - is AvroSchema.RecordSchema -> { + is RecordSchema -> { val recordType = generateRecordClass(schema) listOf(recordType.toFileSpec(schema.name.space)) + // generate nested types schema.fields.flatMap { field -> // the union schema is already generated as a subtype of the record, no need to generate it again. However, we need to generate the types used in the union - if (field.schema is AvroSchema.UnionSchema) { - field.schema.nonNullTypes.flatMap { generateNestedKotlinClasses(it, potentialAnonymousClassName, emptyMap()) } + if (field.schema is UnionSchema) { + (field.schema as UnionSchema).nonNullTypes.flatMap { generateNestedKotlinClasses(it, potentialAnonymousClassName, emptyMap()) } } else { generateNestedKotlinClasses(field.schema, field.name.toPascalCase(), mapOf(schema.asClassName() to recordType)) } } } - is AvroSchema.EnumSchema -> + is EnumSchema -> listOf(generateEnumClass(schema).toFileSpec(schema.name.space)) - is AvroSchema.UnionSchema -> { + is UnionSchema -> { val type = if (schema.isSimpleNullableType) { generateRootValueClass(schema, potentialAnonymousClassName, getTypeName(schema, potentialAnonymousClassName)).toFileSpec(null) @@ -152,7 +169,7 @@ public class KotlinGenerator( listOf(type) + schema.types.flatMap { subType -> generateNestedKotlinClasses(subType, potentialAnonymousClassName, emptyMap()) } } - is AvroSchema.ArraySchema -> { + is ArraySchema -> { val valueClass = generateRootValueClass(schema, potentialAnonymousClassName, getTypeName(schema, potentialAnonymousClassName)) listOf(valueClass.toFileSpec(null)) + ( @@ -161,7 +178,7 @@ public class KotlinGenerator( ) } - is AvroSchema.MapSchema -> { + is MapSchema -> { val mapType = generateRootValueClass( schema, @@ -173,8 +190,8 @@ public class KotlinGenerator( ) + generateNestedKotlinClasses(schema.valueSchema, mapNameFormatter(potentialAnonymousClassName), emptyMap()) } - is AvroSchema.FixedSchema, - is AvroSchema.PrimitiveSchema, + is FixedSchema, + is PrimitiveSchema, -> listOf( generateRootValueClass( @@ -184,13 +201,13 @@ public class KotlinGenerator( ).toFileSpec(null) ) - is AvroSchema.NullSchema -> emptyList() + is NullSchema -> emptyList() } } private fun generateRootValueClass(schema: AvroSchema, className: String, wrappedType: TypeName): TypeSpec { - val nonNullSchema = (schema as? AvroSchema.UnionSchema)?.unwrapIfSimpleNullableType ?: schema - if (nonNullSchema is AvroSchema.UnionSchema) { + val nonNullSchema = (schema as? UnionSchema)?.unwrapIfSimpleNullableType ?: schema + if (nonNullSchema is UnionSchema) { throw IllegalStateException("generateRootValueClass is not for union schema") } return TypeSpec.classBuilder(className) @@ -219,7 +236,7 @@ public class KotlinGenerator( return emptyList() } return when (schema) { - is AvroSchema.RecordSchema -> { + is RecordSchema -> { val recordTypeName = schema.asClassName() if (recordTypeName !in generatedRecords) { val recordType = generateRecordClass(schema) @@ -227,8 +244,8 @@ public class KotlinGenerator( listOf(recordType.toFileSpec(schema.name.space)) + schema.fields.flatMap { field -> // the union schema is already generated as a subtype of the record, no need to generate it again. However, we need to generate the types used in the union - if (field.schema is AvroSchema.UnionSchema) { - field.schema.types.flatMap { generateNestedKotlinClasses(it, potentialAnonymousBaseName, generatedRecords) } + if (field.schema is UnionSchema) { + (field.schema as UnionSchema).types.flatMap { generateNestedKotlinClasses(it, potentialAnonymousBaseName, generatedRecords) } } else { generateNestedKotlinClasses(field.schema, field.name.toPascalCase(), generatedRecords + (schema.asClassName() to recordType)) } @@ -239,10 +256,10 @@ public class KotlinGenerator( } } - is AvroSchema.EnumSchema -> + is EnumSchema -> listOf(generateEnumClass(schema).toFileSpec(schema.name.space)) - is AvroSchema.UnionSchema -> { + is UnionSchema -> { val unionType = if (!schema.isSimpleNullableType) { generateSealedInterface(schema, unionNameFormatter(potentialAnonymousBaseName), potentialAnonymousBaseName) @@ -253,53 +270,54 @@ public class KotlinGenerator( listOfNotNull(unionType) + schema.types.flatMap { subType -> generateNestedKotlinClasses(subType, potentialAnonymousBaseName, generatedRecords) } } - is AvroSchema.ArraySchema -> { + is ArraySchema -> { // assuming the class already exists, nothing to generate schema.actualElementClass?.let { emptyList() } ?: generateNestedKotlinClasses(schema.elementSchema, arrayNameFormatter(potentialAnonymousBaseName), generatedRecords) } - is AvroSchema.MapSchema -> { + is MapSchema -> { generateNestedKotlinClasses(schema.valueSchema, mapNameFormatter(potentialAnonymousBaseName), generatedRecords) } // fixed type is for now set as ByteArray, so nothing to generate - is AvroSchema.FixedSchema, - is AvroSchema.PrimitiveSchema, - is AvroSchema.NullSchema, + is FixedSchema, + is PrimitiveSchema, + is NullSchema, -> emptyList() } } private fun AvroSchema.typeNameFromMetadata(): TypeName? { + if (this !is ResolvedSchema) return null return actualJavaClassName?.let { parseJavaClassName(it) } ?: logicalTypeName?.let { logicalTypes[it] } } private fun getTypeName(schema: AvroSchema, potentialAnonymousBaseName: String): TypeName { return schema.typeNameFromMetadata() ?: when (schema) { - is AvroSchema.RecordSchema, - is AvroSchema.EnumSchema, + is RecordSchema, + is EnumSchema, -> schema.asClassName() - is AvroSchema.StringSchema -> String::class.asClassName() + is StringSchema -> String::class.asClassName() - is AvroSchema.IntSchema -> Int::class.asClassName() + is IntSchema -> Int::class.asClassName() - is AvroSchema.LongSchema -> Long::class.asClassName() + is LongSchema -> Long::class.asClassName() - is AvroSchema.BooleanSchema -> Boolean::class.asClassName() + is BooleanSchema -> Boolean::class.asClassName() - is AvroSchema.FloatSchema -> Float::class.asClassName() + is FloatSchema -> Float::class.asClassName() - is AvroSchema.DoubleSchema -> Double::class.asClassName() + is DoubleSchema -> Double::class.asClassName() - is AvroSchema.BytesSchema, - is AvroSchema.FixedSchema, + is BytesSchema, + is FixedSchema, // To have anything else than a ByteArray for fixed type, the user needs to provide a logical type or a java-class property -> ByteArray::class.asClassName() - is AvroSchema.ArraySchema -> { + is ArraySchema -> { val itemType = schema.actualElementClass?.let { parseJavaClassName(it) }?.nullableIf(schema.elementSchema.isNullable) ?: getTypeName(schema.elementSchema, arrayNameFormatter(potentialAnonymousBaseName)) @@ -307,14 +325,14 @@ public class KotlinGenerator( List::class.asClassName().parameterizedBy(itemType) } - is AvroSchema.MapSchema -> { + is MapSchema -> { val keyType = schema.actualKeyClass?.let { parseJavaClassName(it) } ?: String::class.asClassName() val valueType = getTypeName(schema.valueSchema, mapNameFormatter(potentialAnonymousBaseName)) Map::class.asClassName().parameterizedBy(keyType, valueType) } - is AvroSchema.UnionSchema -> { + is UnionSchema -> { // if the union is a simple nullable type, meaning that it only has null schema and another schema, then we do not generate a union for that val typeName = schema.unwrapIfSimpleNullableType?.let { getTypeName(it, potentialAnonymousBaseName) } @@ -322,7 +340,7 @@ public class KotlinGenerator( typeName.nullableIf(schema.isNullable) } - is AvroSchema.NullSchema -> throw IllegalStateException("Got null schema. Should be handled directly in union") + is NullSchema -> throw IllegalStateException("Got null schema. Should be handled directly in union") } } @@ -340,7 +358,7 @@ public class KotlinGenerator( * } */ private fun generateSealedInterface( - schema: AvroSchema.UnionSchema, + schema: UnionSchema, className: String, potentialAnonymousBaseName: String, ): TypeSpec { @@ -387,7 +405,7 @@ public class KotlinGenerator( * } * ``` */ - private fun generateEnumClass(schema: AvroSchema.EnumSchema): TypeSpec { + private fun generateEnumClass(schema: EnumSchema): TypeSpec { return TypeSpec.enumBuilder(schema.name.simpleName) .addAnnotation(Serializable::class) .addAnnotations(buildAvroPropAnnotations(schema)) @@ -412,7 +430,7 @@ public class KotlinGenerator( .build() } - private fun generateRecordClass(schema: AvroSchema.RecordSchema): TypeSpec { + private fun generateRecordClass(schema: RecordSchema): TypeSpec { return (if (schema.fields.isNotEmpty()) TypeSpec.classBuilder(schema.name.simpleName).addModifiers(KModifier.DATA) else TypeSpec.objectBuilder(schema.name.simpleName)) .addAnnotation(Serializable::class) .addAnnotations(buildAvroPropAnnotations(schema)) @@ -431,7 +449,7 @@ public class KotlinGenerator( } val typeName = getTypeName(field.schema, kotlinFieldName.toPascalCase()) - val nonNullFieldSchema = (field.schema as? AvroSchema.UnionSchema)?.unwrapIfSimpleNullableType ?: field.schema + val nonNullFieldSchema = (field.schema as? UnionSchema)?.unwrapIfSimpleNullableType ?: field.schema builder.addPrimaryProperty( PropertySpec.builder(kotlinFieldName, typeName) @@ -446,12 +464,12 @@ public class KotlinGenerator( } val defaultStr = when { - field.schema is AvroSchema.BytesSchema || - field.schema is AvroSchema.FixedSchema -> - field.defaultValue.jsonPrimitive.content.toByteArray(Charsets.ISO_8859_1).toList() + field.schema is BytesSchema || + field.schema is FixedSchema -> + field.defaultValue!!.jsonPrimitive.content.toByteArray(Charsets.ISO_8859_1).toList() field.defaultValue is JsonPrimitive -> - field.defaultValue.jsonPrimitive.content + field.defaultValue!!.jsonPrimitive.content else -> field.defaultValue.toString() } @@ -476,7 +494,7 @@ public class KotlinGenerator( if (field.defaultValue != null) { if (typeName.isNativelySerializable()) { // TODO recursive types needs to have a default value, or it's not possible to instantiate them - getRecordFieldDefault(field.schema, field.defaultValue) + getRecordFieldDefault(field.schema, field.defaultValue!!) } else { // Non-natively serializable types are from user code, so they also need custom code to instantiate them null @@ -489,9 +507,9 @@ public class KotlinGenerator( } .addTypes( schema.fields.mapNotNull { field -> - if (field.schema is AvroSchema.UnionSchema && !field.schema.isSimpleNullableType) { + if (field.schema is UnionSchema && !(field.schema as UnionSchema).isSimpleNullableType) { val unionBaseName = field.name.toPascalCase() - generateSealedInterface(field.schema, unionNameFormatter(unionBaseName), unionBaseName) + generateSealedInterface(field.schema as UnionSchema, unionNameFormatter(unionBaseName), unionBaseName) } else { null } @@ -503,43 +521,43 @@ public class KotlinGenerator( private fun getRecordFieldDefault(schema: AvroSchema, fieldDefault: JsonElement): CodeBlock? { return when (schema) { - is AvroSchema.FixedSchema, - is AvroSchema.BytesSchema, + is FixedSchema, + is BytesSchema, -> CodeBlock.of("byteArrayOf(%L)", (fieldDefault.jsonPrimitive.content.toByteArray(Charsets.ISO_8859_1)).joinToString(", ")) - is AvroSchema.EnumSchema -> CodeBlock.of("%T.%L", schema.asClassName(), fieldDefault.jsonPrimitive.content) + is EnumSchema -> CodeBlock.of("%T.%L", schema.asClassName(), fieldDefault.jsonPrimitive.content) - is AvroSchema.StringSchema -> CodeBlock.of("%S", fieldDefault.jsonPrimitive.content) + is StringSchema -> CodeBlock.of("%S", fieldDefault.jsonPrimitive.content) - is AvroSchema.BooleanSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.boolean) + is BooleanSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.boolean) - is AvroSchema.DoubleSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.double) + is DoubleSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.double) - is AvroSchema.FloatSchema -> CodeBlock.of("%L", "${fieldDefault.jsonPrimitive.float}f") + is FloatSchema -> CodeBlock.of("%L", "${fieldDefault.jsonPrimitive.float}f") - is AvroSchema.IntSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.int) + is IntSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.int) - is AvroSchema.LongSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.long) + is LongSchema -> CodeBlock.of("%L", fieldDefault.jsonPrimitive.long) // for union, the default has to be of the first type - is AvroSchema.UnionSchema -> getRecordFieldDefault(schema.types.first(), fieldDefault) + is UnionSchema -> getRecordFieldDefault(schema.types.first(), fieldDefault) - is AvroSchema.ArraySchema -> + is ArraySchema -> fieldDefault.jsonArray .map { getRecordFieldDefault(schema.elementSchema, it) ?: return null } .let { getListOfCodeBlock(it) } - is AvroSchema.MapSchema -> + is MapSchema -> fieldDefault.jsonObject .mapValues { getRecordFieldDefault(schema.valueSchema, it.value) ?: return null } .let { getMapOfCodeBlock(it) } - is AvroSchema.NullSchema -> { + is NullSchema -> { fieldDefault.jsonNull CodeBlock.of("null") } - is AvroSchema.RecordSchema -> { + is RecordSchema -> { fieldDefault.jsonObject .also { fieldDefault -> // ensure to have no missing field in default map @@ -576,7 +594,7 @@ private fun TypeName.isNativelySerializable(): Boolean = annotations.none { it.typeName == Contextual::class.asClassName() || it.typeName == Serializable::class.asClassName() } && (this !is ParameterizedTypeName || typeArguments.any { it.isNativelySerializable() }) -private fun AvroSchema.NamedSchema.asClassName() = ClassName(name.space ?: "", name.simpleName) +private fun NamedSchema.asClassName() = ClassName(name.space ?: "", name.simpleName) private fun parseJavaClassName(className: String, kSerializerType: ClassName? = null): TypeName { var shouldBeContextual = false @@ -612,11 +630,20 @@ private fun getBuiltinLogicalTypes() = ) } -private val AvroSchema.UnionSchema.nonNullTypes get() = types.filter { it !is AvroSchema.NullSchema } -private val AvroSchema.UnionSchema.unwrapIfSimpleNullableType: AvroSchema? +private val UnionSchema.nonNullTypes get() = types.filter { it !is NullSchema } +private val UnionSchema.unwrapIfSimpleNullableType: AvroSchema? get() = if (isSimpleNullableType) { - types.first { it !is AvroSchema.NullSchema } + types.first { it !is NullSchema } } else { null - } \ No newline at end of file + } + +private val ResolvedSchema.actualJavaClassName: String? + get() = props["java-class"]?.jsonPrimitive?.contentOrNull + +private val ArraySchema.actualElementClass: String? + get() = props["java-element-class"]?.jsonPrimitive?.contentOrNull + +private val MapSchema.actualKeyClass: String? + get() = props["java-key-class"]?.jsonPrimitive?.contentOrNull \ No newline at end of file diff --git a/kotlin-generator/src/test/expected-sources/record-as-default/BaseRecord.kt b/kotlin-generator/src/test/expected-sources/record-as-default/BaseRecord.kt index 3c966b74..d700f8c8 100644 --- a/kotlin-generator/src/test/expected-sources/record-as-default/BaseRecord.kt +++ b/kotlin-generator/src/test/expected-sources/record-as-default/BaseRecord.kt @@ -12,7 +12,7 @@ import kotlin.OptIn import kotlinx.serialization.Serializable @Serializable -@AvroGenerated("""{"type":"record","name":"BaseRecord","fields":[{"name":"emptyRecordDefault","type":{"type":"record","name":"EmptySubRecord","fields":[]},"doc":"works even for empty record, generating kotlin default","default":{}},{"name":"simpleRecordDefault","type":{"type":"record","name":"SimpleSubRecord","fields":[{"name":"intField","type":"int"},{"name":"floatField","type":"float"},{"name":"stringField","type":"string"},{"name":"bytesField","type":"bytes"},{"name":"fixedField","type":{"type":"fixed","name":"Fixed","size":3}},{"name":"enumField","type":{"type":"enum","name":"Enum","symbols":["A","B"]}},{"name":"recordField","type":{"type":"record","name":"DeepRecord","fields":[{"name":"field","type":"string"}]}}]},"default":{"intField":42,"floatField":36.0,"stringField":"abc","bytesField":"\u0001\u0002\u0003","fixedField":"\u0001\u0002\u0003","enumField":"B","recordField":{"field":"cde"}}},{"name":"recordDefaultWithCustomSerializers","type":{"type":"record","name":"SubRecordWithKSerializer","fields":[{"name":"field","type":{"type":"string","logicalType":"customLogicalTypeWithKSerializer"}}]},"doc":"as this record has custom serializer, it won't generate a kotlin property default","default":{"field":"abc"}},{"name":"recordDefaultWithContextualSerializer","type":{"type":"record","name":"SubRecordWithContextualSerializer","fields":[{"name":"field","type":{"type":"string","logicalType":"contextualLogicalType"}}]},"doc":"as this record has custom serializer, it won't generate a kotlin property default","default":{"field":"abc"}}]}""") +@AvroGenerated("""{"type":"record","name":"BaseRecord","fields":[{"name":"emptyRecordDefault","type":{"type":"record","name":"EmptySubRecord","fields":[]},"doc":"works even for empty record, generating kotlin default","default":{}},{"name":"simpleRecordDefault","type":{"type":"record","name":"SimpleSubRecord","fields":[{"name":"intField","type":"int"},{"name":"floatField","type":"float"},{"name":"stringField","type":"string"},{"name":"bytesField","type":"bytes"},{"name":"fixedField","type":{"type":"fixed","name":"Fixed","size":3}},{"name":"enumField","type":{"type":"enum","name":"Enum","symbols":["A","B"]}},{"name":"recordField","type":{"type":"record","name":"DeepRecord","fields":[{"name":"field","type":"string"}]}}]},"default":{"intField":42,"floatField":36,"stringField":"abc","bytesField":"\u0001\u0002\u0003","fixedField":"\u0001\u0002\u0003","enumField":"B","recordField":{"field":"cde"}}},{"name":"recordDefaultWithCustomSerializers","type":{"type":"record","name":"SubRecordWithKSerializer","fields":[{"name":"field","type":{"type":"string","logicalType":"customLogicalTypeWithKSerializer"}}]},"doc":"as this record has custom serializer, it won't generate a kotlin property default","default":{"field":"abc"}},{"name":"recordDefaultWithContextualSerializer","type":{"type":"record","name":"SubRecordWithContextualSerializer","fields":[{"name":"field","type":{"type":"string","logicalType":"contextualLogicalType"}}]},"doc":"as this record has custom serializer, it won't generate a kotlin property default","default":{"field":"abc"}}]}""") public data class BaseRecord( /** * works even for empty record, generating kotlin default @@ -23,9 +23,9 @@ public data class BaseRecord( @AvroDefault("{}") public val emptyRecordDefault: EmptySubRecord = EmptySubRecord, /** - * Default value: {"intField":42,"floatField":36.0,"stringField":"abc","bytesField":"\u0001\u0002\u0003","fixedField":"\u0001\u0002\u0003","enumField":"B","recordField":{"field":"cde"}} + * Default value: {"intField":42,"floatField":36,"stringField":"abc","bytesField":"\u0001\u0002\u0003","fixedField":"\u0001\u0002\u0003","enumField":"B","recordField":{"field":"cde"}} */ - @AvroDefault("{\"intField\":42,\"floatField\":36.0,\"stringField\":\"abc\",\"bytesField\":\"\\u0001\\u0002\\u0003\",\"fixedField\":\"\\u0001\\u0002\\u0003\",\"enumField\":\"B\",\"recordField\":{\"field\":\"cde\"}}") + @AvroDefault("{\"intField\":42,\"floatField\":36,\"stringField\":\"abc\",\"bytesField\":\"\\u0001\\u0002\\u0003\",\"fixedField\":\"\\u0001\\u0002\\u0003\",\"enumField\":\"B\",\"recordField\":{\"field\":\"cde\"}}") public val simpleRecordDefault: SimpleSubRecord = SimpleSubRecord(intField = 42, floatField = 36.0f, stringField = "abc", bytesField = byteArrayOf(1, 2, 3), fixedField = byteArrayOf(1, 2, 3), enumField = Enum.B, recordField = DeepRecord(`field` = "cde")), /** diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock new file mode 100644 index 00000000..13b62d74 --- /dev/null +++ b/kotlin-js-store/yarn.lock @@ -0,0 +1,2166 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@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.6.1": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" + integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba" + integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@types/cors@^2.8.12": + version "2.8.19" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342" + integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*", "@types/node@>=10.0.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.5.0.tgz#5c99f37c443d9ccc4985866913f1ed364217da31" + integrity sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw== + dependencies: + undici-types "~7.18.0" + +"@types/ws@^8.5.12": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== + +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== + +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" + +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== + +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-3.0.1.tgz#76ac285b9658fa642ce238c276264589aa2b6b57" + integrity sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA== + +"@webpack-cli/info@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-3.0.1.tgz#3cff37fabb7d4ecaab6a8a4757d3826cf5888c63" + integrity sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ== + +"@webpack-cli/serve@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-3.0.1.tgz#bd8b1f824d57e30faa19eb78e4c0951056f72f00" + integrity sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg== + +"@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== + +accepts@~1.3.4: + 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-phases@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== + +acorn@^8.15.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + +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@^5.1.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@^8.0.0, ajv@^8.9.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +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-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + 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== + +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== + +baseline-browser-mapping@^2.9.0: + version "2.10.12" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz#60f9e2172e962839ac313d4e0c8e182090fb6621" + integrity sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +body-parser@^1.19.0: + version "1.20.4" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" + integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== + dependencies: + bytes "~3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "~1.2.0" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + on-finished "~2.4.1" + qs "~6.14.0" + raw-body "~2.5.3" + type-is "~1.6.18" + unpipe "~1.0.0" + +brace-expansion@^1.1.7: + version "1.1.13" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.13.tgz#d37875c01dc9eff988dd49d112a57cb67b54efe6" + integrity sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.3.tgz#0493338bdd58e319b1039c67cf7ee439892c01d9" + integrity sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.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.24.0: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== + dependencies: + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" + +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== + +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-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2, call-bound@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +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.30001759: + version "1.0.30001782" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001782.tgz#f2b8617f998bc134701c54ce9748af44f646e062" + integrity sha512-dZcaJLJeDMh4rELYFw1tvSn1bhZWYFOt468FcbHHxx/Z/dFidd1I6ciyFdi3iwfQCyOjqo9upF6lGQYtMiJWxw== + +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.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + 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" + +chokidar@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +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" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + 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@^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.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.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +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== + +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@^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-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie@~0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cors@~2.8.5: + version "2.8.6" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.6.tgz#ff5dd69bd95e547503820d29aba4f8faf8dfec96" + integrity sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3, cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +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.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +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.3.5, debug@~4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +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== + +depd@2.0.0, 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== + +destroy@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" + integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== + +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" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +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.5.263: + version "1.5.328" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz#d24ce55f1aa5e4a3b877c1b315a0ab40e9498cc8" + integrity sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w== + +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== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +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.2.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" + integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== + +engine.io@~6.6.0: + version "6.6.6" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.6.6.tgz#9942111e7a4dc31f057e73470d7b7fcc7f74c390" + integrity sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA== + dependencies: + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + "@types/ws" "^8.5.12" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.7.2" + cors "~2.8.5" + debug "~4.4.1" + engine.io-parser "~5.2.1" + ws "~8.18.3" + +enhanced-resolve@^5.17.3: + version "5.20.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz#eeeb3966bea62c348c40a0cc9e7912e2557d0be0" + integrity sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.3.0" + +ent@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.2.tgz#22a5ed2fd7ce0cbcff1d1474cf4909a44bdb6e85" + integrity sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + punycode "^1.4.1" + safe-regex-test "^1.1.0" + +envinfo@^7.14.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae" + integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +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== + +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== + +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== + +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.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-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + +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== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-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" + +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.7: + version "3.4.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== + +follow-redirects@^1.0.0: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +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== + +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.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.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +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.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +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@^10.4.5: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.3, 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" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +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, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +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== + +http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + +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" + +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" + +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" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + 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: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +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-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +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-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-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +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== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.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-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + 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@^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.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + 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.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== + dependencies: + glob "^7.1.3" + minimatch "^9.0.3" + webpack-merge "^4.1.5" + +"karma@github:Kotlin/karma#6.4.5": + version "6.4.4" + resolved "https://codeload.github.com/Kotlin/karma/tar.gz/239a8fc984584f0d96b1dd750e7a5e2c79da93a6" + 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.7.2" + 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== + +kotlin-web-helpers@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/kotlin-web-helpers/-/kotlin-web-helpers-3.0.0.tgz#3ed6b48f694f74bb60a737a9d7e2c0e3b29abdb9" + integrity sha512-kdQO4AJQkUPvpLh9aglkXDRyN+CfXO7pKq+GESEnxooBFkQpytLrqZis3ABvmFN1cGw/ZQ/K38u5sRGW+NfBnw== + dependencies: + format-util "^1.0.5" + +loader-runner@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3" + integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q== + +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.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== + +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.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +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== + +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== + +mime-db@1.52.0: + 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.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@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.3, minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== + dependencies: + brace-expansion "^2.0.2" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + +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@11.7.2: + version "11.7.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.2.tgz#3c0079fe5cc2f8ea86d99124debcc42bb1ab22b5" + integrity sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ== + dependencies: + browser-stdout "^1.3.1" + chokidar "^4.0.1" + debug "^4.3.5" + diff "^7.0.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^10.4.5" + he "^1.2.0" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^9.0.5" + ms "^2.1.3" + picocolors "^1.1.1" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^9.2.0" + yargs "^17.7.2" + yargs-parser "^21.1.1" + yargs-unparser "^2.0.0" + +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.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +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-releases@^2.0.27: + version "2.0.36" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.36.tgz#99fd6552aaeda9e17c4713b57a63964a2e325e9d" + integrity sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA== + +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== + +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.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +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-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" + +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" + +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-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== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +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.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-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== + +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" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +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.14.0: + version "6.14.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c" + integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== + dependencies: + side-channel "^1.1.0" + +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: + 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.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +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.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.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.20.0: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +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" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + +"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@^4.3.0, schema-utils@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +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-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +socket.io-adapter@~2.5.2: + version "2.5.6" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz#c697f609d36a676a46749782274607d8df52c1d8" + integrity sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ== + dependencies: + debug "~4.4.1" + ws "~8.18.3" + +socket.io-parser@~4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.6.tgz#19156bf179af3931abd05260cfb1491822578a6f" + integrity sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.4.1" + +socket.io@^4.7.2: + version "4.8.3" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.3.tgz#ca6ba1431c69532e1e0a6f496deebeb601dbc4df" + integrity sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.4.1" + engine.io "~6.6.0" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +source-map-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== + dependencies: + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@0.5.21, 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== + +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== + +statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + 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-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", 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-ansi@^7.0.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + +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== + +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-color@^8.0.0, supports-color@^8.1.1: + 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-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.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.2.tgz#86755feabad08d82a26b891db044808c6ad00f15" + integrity sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA== + +terser-webpack-plugin@^5.3.11: + version "5.4.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz#95fc4cf4437e587be11ecf37d08636089174d76b" + integrity sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + terser "^5.31.1" + +terser@^5.31.1: + version "5.46.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.1.tgz#40e4b1e35d5f13130f82793a8b3eeb7ec3a92eee" + integrity sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.15.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +tmp@^0.2.1: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + +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== + +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" + +ua-parser-js@^0.7.30: + version "0.7.41" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.41.tgz#9f6dee58c389e8afababa62a4a2dc22edb69a452" + integrity sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg== + +undici-types@~7.18.0: + version "7.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" + integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== + +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: + 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.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +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== + +vary@^1: + 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.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102" + integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-6.0.1.tgz#a1ce25da5ba077151afd73adfa12e208e5089207" + integrity sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw== + dependencies: + "@discoveryjs/json-ext" "^0.6.1" + "@webpack-cli/configtest" "^3.0.1" + "@webpack-cli/info" "^3.0.1" + "@webpack-cli/serve" "^3.0.1" + colorette "^2.0.14" + commander "^12.1.0" + cross-spawn "^7.0.3" + envinfo "^7.14.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^6.0.1" + +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@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" + integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.1" + +webpack-sources@^3.3.3: + version "3.3.4" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.4.tgz#a338b95eb484ecc75fbb196cbe8a2890618b4891" + integrity sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q== + +webpack@5.101.3: + version "5.101.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.101.3.tgz#3633b2375bb29ea4b06ffb1902734d977bc44346" + integrity sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.8" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.15.0" + acorn-import-phases "^1.0.3" + browserslist "^4.24.0" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.3" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + 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 "^4.3.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.11" + watchpack "^2.4.1" + webpack-sources "^3.3.3" + +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.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@^9.2.0: + version "9.3.4" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" + integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +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.18.3: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + +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== + +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-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +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.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" + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +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==