From a23c2a650628a5f4d763f284d18d769a5ff51090 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Thu, 11 Jul 2024 18:09:39 +0200 Subject: [PATCH 01/27] Importing cache lib, Update dependencies, and disable specific targets {TMP}Added a new ListPackageSearchEndpoint.kt file to print URLs for various package search API endpoints. Included new repository sources and updated dependency versions in the Gradle build configuration. Also, commented out specific JVM and multiplatform (JS, iOS, macOS, watchOS, tvOS) targets in the build configuration. --- build.gradle.kts | 4 ++ .../src/main/kotlin/build-config.gradle.kts | 30 +++++++------- .../api/v3/http/ListPackageSearchEnpoint.kt | 41 +++++++++++++++++++ packagesearch-api-models.versions.toml | 4 +- settings.gradle.kts | 7 ++++ 5 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt diff --git a/build.gradle.kts b/build.gradle.kts index 71eef30..a23faf8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,11 +10,13 @@ kotlin { api(packageSearchApiModelsVersions.datetime) api(packageSearchApiModelsVersions.kotlinx.serialization.json) api(packageSearchApiModelsVersions.krypto) + api(kotlinxDocumentStore.core) } } jsMain { dependencies { api(npm(packageSearchApiModelsVersions.date.fns)) + api(kotlinxDocumentStore.mvstore) } } jvmTest { @@ -22,6 +24,8 @@ kotlin { implementation(packageSearchApiModelsVersions.junit.jupiter.api) implementation(packageSearchApiModelsVersions.junit.jupiter.params) implementation(packageSearchApiModelsVersions.assertk) + implementation(kotlinxDocumentStore.mvstore) + runtimeOnly(packageSearchApiModelsVersions.junit.jupiter.engine) } } diff --git a/buildSrc/src/main/kotlin/build-config.gradle.kts b/buildSrc/src/main/kotlin/build-config.gradle.kts index 885488c..0069809 100644 --- a/buildSrc/src/main/kotlin/build-config.gradle.kts +++ b/buildSrc/src/main/kotlin/build-config.gradle.kts @@ -14,22 +14,22 @@ plugins { kotlin { explicitApi() jvm { - jvmToolchain(11) +// jvmToolchain(11) } - js(IR) { - browser() - nodejs() - } - iosX64() - iosArm64() - iosSimulatorArm64() - macosArm64() - macosX64() - watchosArm64() - watchosX64() - tvosArm64() - tvosX64() - tvosSimulatorArm64() +// js(IR) { +// browser() +// nodejs() +// } +// iosX64() +// iosArm64() +// iosSimulatorArm64() +// macosArm64() +// macosX64() +// watchosArm64() +// watchosX64() +// tvosArm64() +// tvosX64() +// tvosSimulatorArm64() targets.all { compilations.all { kotlinOptions { diff --git a/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt b/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt new file mode 100644 index 0000000..e817ff1 --- /dev/null +++ b/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt @@ -0,0 +1,41 @@ +package org.jetbrains.packagesearch.api.v3.http + +import io.ktor.http.Url + +public fun main() { + + fun printUrl(url: Url) = println(url.toString()) + + PackageSearchEndpoints.PROD.apply { + printUrl(health) + printUrl(nextScroll) + printUrl(knownRepositories) + printUrl(packageInfoByIdHashes) + printUrl(packageInfoByIds) + printUrl(refreshPackagesInfo) + printUrl(searchPackages) + printUrl(searchProjects) + printUrl(startScroll) + } +} +/* +https://api.prod.package-search.services.jetbrains.com/health +https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/next +https://api.prod.package-search.services.jetbrains.com/known-repositories +https://api.prod.package-search.services.jetbrains.com/package-info-by-id-hashes +https://api.prod.package-search.services.jetbrains.com/package-info-by-ids +https://api.prod.package-search.services.jetbrains.com/refresh-packages-info +https://api.prod.package-search.services.jetbrains.com/search-packages +https://api.prod.package-search.services.jetbrains.com/search-projects +https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/start +https://api.prod.package-search.services.jetbrains.com/health +https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/next +https://api.prod.package-search.services.jetbrains.com/known-repositories +https://api.prod.package-search.services.jetbrains.com/package-info-by-id-hashes +https://api.prod.package-search.services.jetbrains.com/package-info-by-ids +https://api.prod.package-search.services.jetbrains.com/refresh-packages-info +https://api.prod.package-search.services.jetbrains.com/search-packages +https://api.prod.package-search.services.jetbrains.com/search-projects +https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/ + + */ \ No newline at end of file diff --git a/packagesearch-api-models.versions.toml b/packagesearch-api-models.versions.toml index a8dcbf8..7ab6db6 100644 --- a/packagesearch-api-models.versions.toml +++ b/packagesearch-api-models.versions.toml @@ -4,8 +4,8 @@ coroutines = "1.8.0" date-fns = "3.6.0" detekt-gradle-plugin = "1.23.6" junit = "5.10.2" -kotlin = "1.9.23" -kotlinter-gradle = "4.0.0" +kotlin = "2.0.0" +kotlinter-gradle = "4.4.0" kotlinx-datetime = "0.5.0" kotlinx-serialization = "1.6.3" krypto = "4.0.10" diff --git a/settings.gradle.kts b/settings.gradle.kts index b1aa37a..5d3661a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,11 +11,18 @@ rootProject.name = "packagesearch-api-models" dependencyResolutionManagement { repositories { mavenCentral() + maven("https://packages.jetbrains.team/maven/p/kpm/public") + maven { + url = uri("https://plugins.gradle.org/m2/") + } } versionCatalogs { create("packageSearchApiModelsVersions") { from(files("packagesearch-api-models.versions.toml")) } + create("kotlinxDocumentStore") { + from("com.github.lamba92:kotlinx-document-store-version-catalog:1.0.0-SNAPSHOT") + } } } From 41e26d75524e533b64f6005a4b558ccd51e4795b Mon Sep 17 00:00:00 2001 From: fscarponi Date: Fri, 12 Jul 2024 17:49:57 +0200 Subject: [PATCH 02/27] WIP --- .../maven/HttpClientMavenPomProvider.kt | 17 ++- .../packagesearch/maven/PomResolver.kt | 3 +- .../jetbrains/packagesearch/maven/Utils.kt | 5 +- .../commonMain/kotlin/cache/CacheEntries.kt | 47 +++++++ .../commonMain/kotlin/cache/Collections.kt | 31 ++++ .../api/v3/http/PackageSearchApiClient.kt | 133 +++++++++++++----- .../packagesearch/api/v3/http/Utils.kt | 33 ++--- .../packagesearch/api/v3/ApiPackage.kt | 3 +- .../jetbrains/packagesearch/api/v3/ApiScm.kt | 3 +- 9 files changed, 207 insertions(+), 68 deletions(-) create mode 100644 http/client/src/commonMain/kotlin/cache/CacheEntries.kt create mode 100644 http/client/src/commonMain/kotlin/cache/Collections.kt diff --git a/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/HttpClientMavenPomProvider.kt b/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/HttpClientMavenPomProvider.kt index 9c71b5f..913ae8f 100644 --- a/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/HttpClientMavenPomProvider.kt +++ b/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/HttpClientMavenPomProvider.kt @@ -50,23 +50,22 @@ public class HttpClientMavenPomProvider( groupId: String, artifactId: String, version: String, - ): ProjectObjectModel { - return getPomFromMultipleRepositories(groupId, artifactId, version).first() - } + ): ProjectObjectModel = + getPomFromMultipleRepositories(groupId, artifactId, version).first() + override suspend fun getPomFromMultipleRepositories( groupId: String, artifactId: String, version: String, - ): Flow { - return mirrors.asFlow().map { + ): Flow = + mirrors.asFlow().map { it.getPom(groupId, artifactId, version) } - } - override suspend fun getPomByUrl(url: Url): ProjectObjectModel { - return httpClient.get(url).bodyAsPom(xml) - } + override suspend fun getPomByUrl(url: Url): ProjectObjectModel = + httpClient.get(url).bodyAsPom(xml) + private suspend fun MavenUrlBuilder.getPom( groupId: String, diff --git a/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/PomResolver.kt b/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/PomResolver.kt index 0deba5f..64eacc1 100644 --- a/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/PomResolver.kt +++ b/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/PomResolver.kt @@ -76,7 +76,8 @@ public class PomResolver( artifactId: String, version: String, ): ProjectObjectModel? = - pomProvider.getPomFromMultipleRepositories(groupId, artifactId, version) + pomProvider + .getPomFromMultipleRepositories(groupId, artifactId, version) .firstOrNull() ?.let { resolve(it) } diff --git a/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/Utils.kt b/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/Utils.kt index 3ebbd6d..8816f80 100644 --- a/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/Utils.kt +++ b/build-systems/src/commonMain/kotlin/org/jetbrains/packagesearch/maven/Utils.kt @@ -186,7 +186,10 @@ public object GoogleMavenCentralMirror : MavenUrlBuilder { ) } -internal data class DependencyKey(val groupId: String, val artifactId: String) +internal data class DependencyKey( + val groupId: String, + val artifactId: String +) /** * Evaluates the value of the provided project property within the given model accessor. diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt new file mode 100644 index 0000000..9016d22 --- /dev/null +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -0,0 +1,47 @@ +package cache + +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.jetbrains.packagesearch.api.v3.ApiPackage +import org.jetbrains.packagesearch.api.v3.ApiRepository +import org.jetbrains.packagesearch.api.v3.http.SearchPackagesRequest +import kotlin.time.Duration.Companion.hours + +@Serializable +internal data class ApiRepositoryCacheEntry( + val values: List, + +) { + val expires: Instant = Clock.System.now().plus(6.hours) + val isExpired: Boolean + get() = expires < Clock.System.now() +} + + +@Serializable +internal data class ApiPackageCacheEntry( + val apiPackage: ApiPackage +) { + val id: String = apiPackage.id + @SerialName("_id")val idHash: String = apiPackage.idHash + + val expires: Instant = Clock.System.now().plus(12.hours) + val isExpired: Boolean + get() = expires < Clock.System.now() +} + +@Serializable +internal data class SearchPackageRequestEntry( + val request: SearchPackagesRequest, + val packages: List +) { + val searchQuery: String = request.searchQuery + +// @SerialName("_id")val search: String = request.hashCode() + + val expires: Instant = Clock.System.now().plus(12.hours) + val isExpired: Boolean + get() = expires < Clock.System.now() +} \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/cache/Collections.kt b/http/client/src/commonMain/kotlin/cache/Collections.kt new file mode 100644 index 0000000..52d4f54 --- /dev/null +++ b/http/client/src/commonMain/kotlin/cache/Collections.kt @@ -0,0 +1,31 @@ +package cache + +import kotlinx.document.database.DataStore +import kotlinx.document.database.KotlinxDocumentDatabase +import kotlinx.document.database.getObjectCollection + +internal class CacheDB(val dataStore: DataStore) { + val db = KotlinxDocumentDatabase { + store = dataStore + } + + suspend fun apiRepositoryCache() = + db.getObjectCollection("API_REPOSITORIES") + + suspend fun apiPackagesCache() = + db.getObjectCollection("PACKAGES").apply { + getAllIndexNames().ifEmpty { + createIndex(ApiPackageCacheEntry::id.name) + createIndex(ApiPackageCacheEntry::idHash.name) + } + } + + suspend fun searchPackageCache()= + db.getObjectCollection("SEARCH_PACKAGES").apply { + getAllIndexNames().ifEmpty { + createIndex(SearchPackageRequestEntry::searchQuery.name) + } + } + + +} \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index cc71b7e..00848eb 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -1,36 +1,26 @@ package org.jetbrains.packagesearch.api.v3.http -import io.ktor.client.HttpClient -import io.ktor.client.HttpClientConfig -import io.ktor.client.call.body -import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.engine.HttpClientEngineConfig -import io.ktor.client.engine.HttpClientEngineFactory -import io.ktor.client.plugins.HttpCallValidator -import io.ktor.client.plugins.HttpRequestRetry -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.plugins.compression.ContentEncoding -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import cache.ApiPackageCacheEntry +import cache.ApiRepositoryCacheEntry +import cache.CacheDB +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.engine.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.compression.* +import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.request.HttpRequestBuilder import io.ktor.client.request.get import io.ktor.client.request.request import io.ktor.client.request.setBody -import io.ktor.client.statement.HttpResponse -import io.ktor.client.statement.bodyAsText -import io.ktor.client.statement.request -import io.ktor.http.ContentType -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpMethod -import io.ktor.http.HttpStatusCode -import io.ktor.http.Url -import io.ktor.http.isSuccess -import io.ktor.serialization.kotlinx.json.json -import io.ktor.serialization.kotlinx.protobuf.protobuf -import io.ktor.util.AttributeKey -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* +import io.ktor.serialization.kotlinx.protobuf.* +import io.ktor.util.* +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.document.database.DataStore import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.protobuf.ProtoBuf @@ -44,8 +34,14 @@ import kotlin.time.Duration.Companion.seconds public class PackageSearchApiClient( public val endpoints: PackageSearchEndpoints, private val httpClient: HttpClient = defaultHttpClient(), + dataStore: DataStore, ) { + private val cachedb = CacheDB(dataStore) + + private suspend fun isOffline() = httpClient.isOffline() + + @Serializable private data class Error(val error: Inner) { @Serializable @@ -146,39 +142,96 @@ public class PackageSearchApiClient( cache: Boolean = true, ) = defaultRawRequest(method, url, body, requestBuilder, cache).body() - public suspend fun getKnownRepositories(requestBuilder: (HttpRequestBuilder.() -> Unit)? = null): List = - httpClient.request(endpoints.knownRepositories) { + public suspend fun getKnownRepositories(requestBuilder: (HttpRequestBuilder.() -> Unit)? = null): List { + val apiRepositoryCache = cachedb.apiRepositoryCache() + + apiRepositoryCache.iterateAll() + .firstOrNull() + ?.let { if (isOffline() || !it.isExpired) return it.values } + + val results = httpClient.request(endpoints.knownRepositories) { method = HttpMethod.Get header(HttpHeaders.Accept, ContentType.Application.Json) requestBuilder?.invoke(this) }.body>() + apiRepositoryCache.clear() + apiRepositoryCache.insert(ApiRepositoryCacheEntry(results)) + return results + } + + public suspend fun getPackageInfoByIds( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - defaultRequest<_, List>( - method = HttpMethod.Post, - url = endpoints.packageInfoByIds, - body = GetPackageInfoRequest(ids), - requestBuilder = requestBuilder, - ).associateBy { it.id } + fetchPackageInfo(ids, "id", endpoints.packageInfoByIds, requestBuilder) public suspend fun getPackageInfoByIdHashes( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - defaultRequest<_, List>( + fetchPackageInfo(ids, "_id", endpoints.packageInfoByIds, requestBuilder) + + // Common Function to Fetch Package Info (Handles Both ID and ID Hash Retrieval) + private suspend fun fetchPackageInfo( + identifiers: Set, + lookupField: String, // "_id" for idHash, "id" for id + endpointUrl: Url, // Differentiate between the two endpoints + requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, + ): Map = coroutineScope { + + val packageInfoCache = cachedb.apiPackagesCache() + + val cachedResults = + identifiers + .map { id -> + async { + packageInfoCache + .find(lookupField, id) + .firstOrNull() + ?.takeIf { isOffline() || !it.isExpired } + ?.let { id to it.apiPackage } // Pair the ID with the cache entry for easier processing + } + } + .awaitAll() + .filterNotNull() + .toMap() + + val unresolvedIdentifiers = identifiers - cachedResults.keys + + if (unresolvedIdentifiers.isEmpty()) { + return@coroutineScope cachedResults + } + + val onlineResults = defaultRequest<_, List>( method = HttpMethod.Post, - url = endpoints.packageInfoByIdHashes, - body = GetPackageInfoRequest(ids), + url = endpointUrl, + body = GetPackageInfoRequest(unresolvedIdentifiers), requestBuilder = requestBuilder, ).associateBy { it.id } + onlineResults.values.forEach { + packageInfoCache.insert(ApiPackageCacheEntry(it)) + } + + // Combine Results + onlineResults + cachedResults + } + public suspend fun searchPackages( request: SearchPackagesRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, - ): List = + ): List { + val searchCache= cachedb.searchPackageCache() + searchCache.find("searchQuery", request.searchQuery) + .filter { it.request.packagesType.any { it in request.packagesType } } + //filter and delete the exiped one + .toList() + .partition { !it.isExpired } + //find the most +// .let { if (isOffline() || !it.isExpired) return it.request.searchQuery } + defaultRequest<_, List>( method = HttpMethod.Post, url = endpoints.searchPackages, @@ -186,6 +239,8 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ) + } + public suspend fun startScroll( request: SearchPackagesStartScrollRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt index c0d94d1..8ecc2d7 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt @@ -1,22 +1,15 @@ package org.jetbrains.packagesearch.api.v3.http -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.request.HttpRequest -import io.ktor.http.HttpMessageBuilder -import io.ktor.http.HttpStatusCode -import io.ktor.http.URLBuilder -import io.ktor.http.Url -import io.ktor.util.toMap -import kotlin.time.Duration -import kotlin.time.Duration.Companion.milliseconds +import io.ktor.client.* +import io.ktor.client.plugins.* +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.util.* import kotlinx.serialization.Serializable import org.jetbrains.packagesearch.api.v3.ApiPackage -import org.jetbrains.packagesearch.api.v3.search.NextScrollParametersBuilder -import org.jetbrains.packagesearch.api.v3.search.SearchParametersBuilder -import org.jetbrains.packagesearch.api.v3.search.StartScrollParametersBuilder -import org.jetbrains.packagesearch.api.v3.search.buildNextScrollParameters -import org.jetbrains.packagesearch.api.v3.search.buildSearchParameters -import org.jetbrains.packagesearch.api.v3.search.buildStartScrollParameters +import org.jetbrains.packagesearch.api.v3.search.* +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds public fun buildUrl(action: URLBuilder.() -> Unit): Url = URLBuilder().apply(action).build() @@ -54,4 +47,12 @@ public data class SerializableHttpRequest( val headers: Map>, ) -public fun HttpRequest.toSerializable(): SerializableHttpRequest = SerializableHttpRequest(url.toString(), method.value, headers.toMap()) +public fun HttpRequest.toSerializable(): SerializableHttpRequest = + SerializableHttpRequest(url.toString(), method.value, headers.toMap()) + +internal suspend fun HttpClient.isOffline() = !isOnline() + +internal suspend fun HttpClient.isOnline() = + head("https://www.jetbrains.com").status.isSuccess() + + diff --git a/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiPackage.kt b/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiPackage.kt index 1f93d5d..10d0dde 100644 --- a/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiPackage.kt +++ b/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiPackage.kt @@ -27,7 +27,8 @@ public sealed interface ApiPackage { public companion object { public fun hashPackageId(id: String): String = - SHA256.create() + SHA256 + .create() .update(id.encodeToByteArray()) .digest() .hex diff --git a/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiScm.kt b/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiScm.kt index 640c9bb..405d657 100644 --- a/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiScm.kt +++ b/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/ApiScm.kt @@ -19,7 +19,8 @@ public sealed interface ApiScm { public companion object { public fun hashScmUrl(url: String): String = - SHA256.create() + SHA256 + .create() .update(url.encodeToByteArray()) .digest() .hex From 3f92521ebe19251f594a3e712c09f74d40409d74 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Mon, 15 Jul 2024 12:18:23 +0200 Subject: [PATCH 03/27] Add HTTP request tests for package search API Created new `testAPI.http` with multiple endpoints for testing package search API requests, including package info retrieval, known repositories, and package search functionalities. This ensures better coverage and easier testing of different API interactions. --- testAPI.http | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 testAPI.http diff --git a/testAPI.http b/testAPI.http new file mode 100644 index 0000000..2cab87d --- /dev/null +++ b/testAPI.http @@ -0,0 +1,84 @@ +POST https://api.prod.package-search.services.jetbrains.com/package-info-by-id-hashes +Content-Type: application/json + +{ + "ids": ["afa0f79b67522a855aa1343ed55939170e5910020c7e39f081c59f38f132c0cf"] +} + +### +GET https://api.prod.package-search.services.jetbrains.com/known-repositories +Accept: application/json + +### + +POST https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/start +Content-Type: application/json + +{ + "packagesType": [ ], + "searchQuery": "ktor", + "batchSize" : 10, + "duration": "2m" +} + +### +POST https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/next +Content-Type: application/json + +{ + "scrollId": "H4sIAAAAAAAA/y3Qy26qQACA4XdhSxNGELAmZ8FFUEEcbgJumkEQBrDDvTBN3/00Oeff/dvvmxlK1Gfa1A+kH5j997//ABtmzxy3w0n5HydGXhBy+kVZTW47wyi78S4m7wVwoazoR5Tk7mSNBqwJC+yu5US+VirfBZekHRtOfiAqqI5lBfU5vcXxfaOGui1RvbFYOx/5kEZ8pzv0TOti59qjdcxS77NY4oArIuBmJBAVzWHDIjAvqCyz3si9s7mJtFRmDVhKEPDkcYRPEoIMdlfflKTVC07h3GWChmTlmmGq71rUdeadN6JqTtVV9uVg9ctWKGIyfc2sOgPlBb9oPiVEjPMULktDnj2ODdVmD/WEja1suk66w/5qv0TuFEeIFFf5UT+izjbrtbUcNq4WWTwcLtLtScX3vknaExR+9e7VWfOSV3rXOeu6TthrHUFVs+Wz2inYRfTZhNov8x/m541p0DDa+IVHZr8BP38BJ1soAaMBAAA=", + "duration": "2m" +} + +### +POST https://api.prod.package-search.services.jetbrains.com/search-packages +Content-Type: application/json +JB-Plugin-Version: 241.0.12 + +{ + "packagesType": [ ], + "searchQuery": "ktor" +} + +### +POST https://api.prod.package-search.services.jetbrains.com/search-projects +Content-Type: application/json + +{ + "query": "ktor", + "onlyStable": true +} + + + +### +POST https://api.dev.package-search.services.jetbrains.com/refresh-packages-info +Content-Type: application/json +JB-Plugin-Version: 241.0.9 +#JB-IDE-Version: test + +{ + "packages": [ + { + "packageIdHash": "726e58b2eca1ac6c63b6e923c67630aaa59a23a37969efd3059982adae27e90b", + "latestKnownVersion": null + }, + { + "packageIdHash": "67f3a6fb77bc58f1fa43027fbe89e2804e1f38bfbc3bca52e051dd7a0fb0a078", + "latestKnownVersion": null + } + ] +} + +### +POST https://api.dev.package-search.services.jetbrains.com/package-info-by-ids +Content-Type: application/json +JB-Plugin-Version: 241.0.9 + + +{ + "ids": [ + "maven:io.ktor:ktor-client-core" + ] +} + From 875a532740caee6ed2c143a5778f728344789c76 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 16 Jul 2024 15:23:49 +0200 Subject: [PATCH 04/27] update cache system and add /health endpoint to testApi.http --- .../commonMain/kotlin/cache/CacheEntries.kt | 44 ++++- .../commonMain/kotlin/cache/Collections.kt | 14 +- .../api/v3/http/PackageSearchApiClient.kt | 155 ++++++++++++------ .../packagesearch/api/v3/http/Http.kt | 2 +- testAPI.http | 4 + 5 files changed, 163 insertions(+), 56 deletions(-) diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index 9016d22..329d412 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -5,16 +5,22 @@ import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import org.jetbrains.packagesearch.api.v3.ApiPackage +import org.jetbrains.packagesearch.api.v3.ApiProject import org.jetbrains.packagesearch.api.v3.ApiRepository import org.jetbrains.packagesearch.api.v3.http.SearchPackagesRequest +import kotlin.time.Duration import kotlin.time.Duration.Companion.hours + +public val DEFAULT_EXPIRATION_TIME: Duration = 12.hours +public val SHORT_EXPIRATION_TIME: Duration = 6.hours + @Serializable internal data class ApiRepositoryCacheEntry( + val _id: Long? = null, val values: List, - ) { - val expires: Instant = Clock.System.now().plus(6.hours) + val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } @@ -22,26 +28,50 @@ internal data class ApiRepositoryCacheEntry( @Serializable internal data class ApiPackageCacheEntry( + val _id: Long? = null, val apiPackage: ApiPackage ) { val id: String = apiPackage.id - @SerialName("_id")val idHash: String = apiPackage.idHash - val expires: Instant = Clock.System.now().plus(12.hours) + @SerialName("_id") + val idHash: String = apiPackage.idHash + + val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } @Serializable -internal data class SearchPackageRequestEntry( +internal data class SearchPackageRequestCacheEntry( + val _id: Long? = null, val request: SearchPackagesRequest, val packages: List ) { val searchQuery: String = request.searchQuery -// @SerialName("_id")val search: String = request.hashCode() + val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) + val isExpired: Boolean + get() = expires < Clock.System.now() +} + +@Serializable +internal data class SearchPackageScrollCacheEntry( + val _id: Long? = null, + val scrollId: String, + val packages: List +) { + val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) + val isExpired: Boolean + get() = expires < Clock.System.now() +} - val expires: Instant = Clock.System.now().plus(12.hours) +@Serializable +internal data class ApiProjectsCacheEntry( + val _id: Long? = null, + val queryString: String, + val values: List +) { + val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/cache/Collections.kt b/http/client/src/commonMain/kotlin/cache/Collections.kt index 52d4f54..5a7ed7e 100644 --- a/http/client/src/commonMain/kotlin/cache/Collections.kt +++ b/http/client/src/commonMain/kotlin/cache/Collections.kt @@ -21,9 +21,19 @@ internal class CacheDB(val dataStore: DataStore) { } suspend fun searchPackageCache()= - db.getObjectCollection("SEARCH_PACKAGES").apply { + db.getObjectCollection("SEARCH_PACKAGES").apply { getAllIndexNames().ifEmpty { - createIndex(SearchPackageRequestEntry::searchQuery.name) + createIndex(SearchPackageRequestCacheEntry::searchQuery.name) + } + } + + suspend fun scrollStartPackageCache() = + db.getObjectCollection("SCROLL_PACKAGES") + + suspend fun apiProjectsCache() = + db.getObjectCollection("API_PROJECTS").apply { + getAllIndexNames().ifEmpty { + createIndex(ApiProjectsCacheEntry::queryString.name) } } diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 00848eb..94550ac 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -1,8 +1,11 @@ package org.jetbrains.packagesearch.api.v3.http import cache.ApiPackageCacheEntry +import cache.ApiProjectsCacheEntry import cache.ApiRepositoryCacheEntry import cache.CacheDB +import cache.SearchPackageRequestCacheEntry +import cache.SearchPackageScrollCacheEntry import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.* @@ -35,12 +38,23 @@ public class PackageSearchApiClient( public val endpoints: PackageSearchEndpoints, private val httpClient: HttpClient = defaultHttpClient(), dataStore: DataStore, + coroutineScope: CoroutineScope, + onlineCheckInterval: Duration = 1.minutes ) { private val cachedb = CacheDB(dataStore) - private suspend fun isOffline() = httpClient.isOffline() + private val _onlineStateFlow = MutableStateFlow(true) + public val onlineStateFlow: StateFlow = _onlineStateFlow + init { + coroutineScope.launch { + while (true) { + _onlineStateFlow.emit(checkOnlineState()) + delay(onlineCheckInterval) + } + } + } @Serializable private data class Error(val error: Inner) { @@ -144,10 +158,12 @@ public class PackageSearchApiClient( public suspend fun getKnownRepositories(requestBuilder: (HttpRequestBuilder.() -> Unit)? = null): List { val apiRepositoryCache = cachedb.apiRepositoryCache() + val isOffline = !onlineStateFlow.value - apiRepositoryCache.iterateAll() + val searchResult = apiRepositoryCache.iterateAll() .firstOrNull() - ?.let { if (isOffline() || !it.isExpired) return it.values } + + searchResult?.let { if (isOffline || !it.isExpired) return it.values } val results = httpClient.request(endpoints.knownRepositories) { method = HttpMethod.Get @@ -155,8 +171,7 @@ public class PackageSearchApiClient( requestBuilder?.invoke(this) }.body>() - apiRepositoryCache.clear() - apiRepositoryCache.insert(ApiRepositoryCacheEntry(results)) + apiRepositoryCache.insert(ApiRepositoryCacheEntry(searchResult?._id, results)) return results } @@ -171,27 +186,28 @@ public class PackageSearchApiClient( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - fetchPackageInfo(ids, "_id", endpoints.packageInfoByIds, requestBuilder) + fetchPackageInfo(ids, "idHash", endpoints.packageInfoByIds, requestBuilder) // Common Function to Fetch Package Info (Handles Both ID and ID Hash Retrieval) private suspend fun fetchPackageInfo( identifiers: Set, - lookupField: String, // "_id" for idHash, "id" for id + lookupField: String, // "idHash" or "id" endpointUrl: Url, // Differentiate between the two endpoints requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = coroutineScope { val packageInfoCache = cachedb.apiPackagesCache() + val isOffline = !onlineStateFlow.value val cachedResults = identifiers - .map { id -> + .map { id -> // NOTE: id depends on the lookupField async { packageInfoCache .find(lookupField, id) .firstOrNull() - ?.takeIf { isOffline() || !it.isExpired } - ?.let { id to it.apiPackage } // Pair the ID with the cache entry for easier processing + ?.takeIf { isOffline || !it.isExpired } + ?.let { id to it } // Pair the ID with the cache entry for easier processing } } .awaitAll() @@ -201,7 +217,7 @@ public class PackageSearchApiClient( val unresolvedIdentifiers = identifiers - cachedResults.keys if (unresolvedIdentifiers.isEmpty()) { - return@coroutineScope cachedResults + return@coroutineScope cachedResults.mapValues { it.value.apiPackage } } val onlineResults = defaultRequest<_, List>( @@ -209,48 +225,78 @@ public class PackageSearchApiClient( url = endpointUrl, body = GetPackageInfoRequest(unresolvedIdentifiers), requestBuilder = requestBuilder, - ).associateBy { it.id } + ).associateBy { if (lookupField == "id") it.id else it.idHash } onlineResults.values.forEach { - packageInfoCache.insert(ApiPackageCacheEntry(it)) + val cacheKey = if (lookupField == "id") it.id else it.idHash + packageInfoCache.insert( + ApiPackageCacheEntry( + _id = cachedResults[cacheKey]?._id, + it + ) + ) } // Combine Results - onlineResults + cachedResults + onlineResults + cachedResults.mapValues { it.value.apiPackage } } public suspend fun searchPackages( request: SearchPackagesRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): List { - val searchCache= cachedb.searchPackageCache() - searchCache.find("searchQuery", request.searchQuery) - .filter { it.request.packagesType.any { it in request.packagesType } } - //filter and delete the exiped one - .toList() - .partition { !it.isExpired } - //find the most -// .let { if (isOffline() || !it.isExpired) return it.request.searchQuery } + val searchCache = cachedb.searchPackageCache() - defaultRequest<_, List>( + val isOffline = !onlineStateFlow.value + val searchResult = searchCache.find("searchQuery", request.searchQuery) + .firstOrNull() + + searchResult + ?.takeIf { isOffline || !it.isExpired } + ?.takeIf { it.request.packagesType.toSet().containsAll(request.packagesType.toSet()) } + ?.let { return it.packages } + + // cache result not found or expired or not exhaustive enough + + return defaultRequest<_, List>( method = HttpMethod.Post, url = endpoints.searchPackages, body = request, requestBuilder = requestBuilder, - ) + ).also { + searchCache.insert(SearchPackageRequestCacheEntry(searchResult?._id, request, it)) + } } public suspend fun startScroll( request: SearchPackagesStartScrollRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, - ): SearchPackagesScrollResponse = - defaultRequest<_, SearchPackagesScrollResponse>( + ): SearchPackagesScrollResponse { + val cache = cachedb.scrollStartPackageCache() + + val cacheResult = cache + .find("searchQuery", request.searchQuery) + .firstOrNull() + ?.also { + if (!it.isExpired) return SearchPackagesScrollResponse(it.scrollId, it.packages) + } + + return defaultRequest<_, SearchPackagesScrollResponse>( method = HttpMethod.Post, url = endpoints.startScroll, body = request, requestBuilder = requestBuilder, - ) + ).also { + cache.insert( + SearchPackageScrollCacheEntry( + _id = cacheResult?._id, + scrollId = it.scrollId, + packages = it.data + ) + ) + } + } public suspend fun nextScroll( request: SearchPackagesNextScrollRequest, @@ -266,13 +312,31 @@ public class PackageSearchApiClient( public suspend fun searchProjects( request: SearchProjectRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)?, - ): List = - defaultRequest<_, List>( + ): List { + val apiProjectsCache = cachedb.apiProjectsCache() + + val cacheResult = apiProjectsCache.find("queryString", request.query) + .firstOrNull() + ?.also { if (!it.isExpired) return it.values } + + return defaultRequest<_, List>( method = HttpMethod.Post, url = endpoints.searchPackages, body = request, requestBuilder = requestBuilder, - ) + ).also { + + apiProjectsCache.insert( + ApiProjectsCacheEntry( + _id = cacheResult?._id, + queryString = request.query, + values = it + ) + ) + } + + } + public suspend fun refreshPackagesInfo( request: RefreshPackagesInfoRequest, @@ -285,21 +349,20 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ) - public fun isOnlineFlow(pollingInterval: Duration = 1.minutes): Flow = - flow { - while (true) { - val request = - kotlin.runCatching { - httpClient.get(endpoints.health) { - header(HttpHeaders.Accept, ContentType.Text.Plain) - } - } - val isOnline = - request - .map { it.status.isSuccess() } - .getOrDefault(false) - emit(isOnline) - delay(pollingInterval) + private suspend fun checkOnlineState(): Boolean { + val request = + kotlin.runCatching { + httpClient.get(endpoints.health) { + header(HttpHeaders.Accept, ContentType.Text.Plain) + } } - } + val isOnline = + request + .map { it.status.isSuccess() } + .getOrDefault(false) + return isOnline + } + } + + diff --git a/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Http.kt b/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Http.kt index 30a5157..f37e940 100644 --- a/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Http.kt +++ b/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Http.kt @@ -53,7 +53,7 @@ public data class SearchPackagesNextScrollRequest( @Serializable public data class SearchPackagesScrollResponse( - val scrollId: String?, + val scrollId: String, val data: List, ) diff --git a/testAPI.http b/testAPI.http index 2cab87d..b30f231 100644 --- a/testAPI.http +++ b/testAPI.http @@ -1,3 +1,7 @@ +GET https://api.prod.package-search.services.jetbrains.com/health +Accept: application/json + +### POST https://api.prod.package-search.services.jetbrains.com/package-info-by-id-hashes Content-Type: application/json From 7fd15a5bfaf7af4440f4ae8ebd49ea83846420cf Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 17 Jul 2024 12:03:11 +0200 Subject: [PATCH 05/27] Update and improve HttpClient's cache system and add matching tests This update addresses an issue in the HttpClient's cache refresh, wherein previously expired results were not being updated. Changes were made primarily in the fetchPackageInfo method to rectify this. Additionally, this commit includes new tests for the HttpClient cache system to verify its correct operation post-fix. --- http/client/build.gradle.kts | 13 + .../commonMain/kotlin/cache/CacheEntries.kt | 21 +- .../api/v3/http/PackageSearchApiClient.kt | 105 +- .../src/commonTest/kotlin/CacheTests.kt | 132 + http/client/src/commonTest/kotlin/Utils.kt | 6 + .../resources/package-info-by-ids-ktor.json | 15073 ++++++++++++++++ packagesearch-api-models.versions.toml | 3 + 7 files changed, 15307 insertions(+), 46 deletions(-) create mode 100644 http/client/src/commonTest/kotlin/CacheTests.kt create mode 100644 http/client/src/commonTest/kotlin/Utils.kt create mode 100644 http/client/src/commonTest/resources/package-info-by-ids-ktor.json diff --git a/http/client/build.gradle.kts b/http/client/build.gradle.kts index 0d8d9e9..66bbfc1 100644 --- a/http/client/build.gradle.kts +++ b/http/client/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.kotlin.dsl.kotlin + plugins { `build-config` } @@ -23,6 +25,17 @@ kotlin { api(packageSearchApiModelsVersions.kotlinx.serialization.json) } } + commonTest{ + dependencies{ + api(kotlin("test-junit5")) + api(packageSearchApiModelsVersions.kotlinx.coroutines.test) + api(packageSearchApiModelsVersions.ktor.client.mock) + api(packageSearchApiModelsVersions.ktor.client.logging) + api(packageSearchApiModelsVersions.junit.jupiter.engine) + api(packageSearchApiModelsVersions.mvstore) + api(kotlinxDocumentStore.mvstore) + } + } jvmMain { dependencies { api(packageSearchApiModelsVersions.logback.classic) diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index 329d412..a751a3c 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -2,7 +2,6 @@ package cache import kotlinx.datetime.Clock import kotlinx.datetime.Instant -import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiProject @@ -19,8 +18,8 @@ public val SHORT_EXPIRATION_TIME: Duration = 6.hours internal data class ApiRepositoryCacheEntry( val _id: Long? = null, val values: List, -) { val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) +) { val isExpired: Boolean get() = expires < Clock.System.now() } @@ -29,14 +28,12 @@ internal data class ApiRepositoryCacheEntry( @Serializable internal data class ApiPackageCacheEntry( val _id: Long? = null, - val apiPackage: ApiPackage + val apiPackage: ApiPackage, + val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) ) { val id: String = apiPackage.id - @SerialName("_id") val idHash: String = apiPackage.idHash - - val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } @@ -45,11 +42,11 @@ internal data class ApiPackageCacheEntry( internal data class SearchPackageRequestCacheEntry( val _id: Long? = null, val request: SearchPackagesRequest, - val packages: List + val packages: List, + val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) ) { val searchQuery: String = request.searchQuery - val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } @@ -58,9 +55,9 @@ internal data class SearchPackageRequestCacheEntry( internal data class SearchPackageScrollCacheEntry( val _id: Long? = null, val scrollId: String, - val packages: List + val packages: List, + val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) ) { - val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } @@ -69,9 +66,9 @@ internal data class SearchPackageScrollCacheEntry( internal data class ApiProjectsCacheEntry( val _id: Long? = null, val queryString: String, - val values: List + val values: List, + val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) ) { - val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) val isExpired: Boolean get() = expires < Clock.System.now() } \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 94550ac..631749e 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -30,32 +30,29 @@ import kotlinx.serialization.protobuf.ProtoBuf import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiProject import org.jetbrains.packagesearch.api.v3.ApiRepository +import kotlin.collections.map import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds public class PackageSearchApiClient( - public val endpoints: PackageSearchEndpoints, - private val httpClient: HttpClient = defaultHttpClient(), dataStore: DataStore, - coroutineScope: CoroutineScope, + private val httpClient: HttpClient = defaultHttpClient(), + public val endpoints: PackageSearchEndpoints, + private val coroutineScope: CoroutineScope = CoroutineScope(httpClient.coroutineContext), onlineCheckInterval: Duration = 1.minutes ) { - private val cachedb = CacheDB(dataStore) + private val cacheDB = CacheDB(dataStore) private val _onlineStateFlow = MutableStateFlow(true) public val onlineStateFlow: StateFlow = _onlineStateFlow init { - coroutineScope.launch { - while (true) { - _onlineStateFlow.emit(checkOnlineState()) - delay(onlineCheckInterval) - } - } + initiateOnlineCheckJob(onlineCheckInterval) } + @Serializable private data class Error(val error: Inner) { @Serializable @@ -157,13 +154,11 @@ public class PackageSearchApiClient( ) = defaultRawRequest(method, url, body, requestBuilder, cache).body() public suspend fun getKnownRepositories(requestBuilder: (HttpRequestBuilder.() -> Unit)? = null): List { - val apiRepositoryCache = cachedb.apiRepositoryCache() + val apiRepositoryCache = cacheDB.apiRepositoryCache() val isOffline = !onlineStateFlow.value + val cachedResult = apiRepositoryCache.iterateAll().firstOrNull() - val searchResult = apiRepositoryCache.iterateAll() - .firstOrNull() - - searchResult?.let { if (isOffline || !it.isExpired) return it.values } + cachedResult?.let { if (isOffline || !it.isExpired) return it.values } val results = httpClient.request(endpoints.knownRepositories) { method = HttpMethod.Get @@ -171,7 +166,7 @@ public class PackageSearchApiClient( requestBuilder?.invoke(this) }.body>() - apiRepositoryCache.insert(ApiRepositoryCacheEntry(searchResult?._id, results)) + apiRepositoryCache.insert(ApiRepositoryCacheEntry(cachedResult?._id, results)) return results } @@ -196,17 +191,16 @@ public class PackageSearchApiClient( requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = coroutineScope { - val packageInfoCache = cachedb.apiPackagesCache() + val apiPackageCacheDB = cacheDB.apiPackagesCache() val isOffline = !onlineStateFlow.value val cachedResults = identifiers .map { id -> // NOTE: id depends on the lookupField async { - packageInfoCache + apiPackageCacheDB .find(lookupField, id) .firstOrNull() - ?.takeIf { isOffline || !it.isExpired } ?.let { id to it } // Pair the ID with the cache entry for easier processing } } @@ -214,10 +208,12 @@ public class PackageSearchApiClient( .filterNotNull() .toMap() - val unresolvedIdentifiers = identifiers - cachedResults.keys + val validResults = if (!isOffline) cachedResults.filter { !it.value.isExpired } else cachedResults + + val unresolvedIdentifiers = identifiers - validResults.keys if (unresolvedIdentifiers.isEmpty()) { - return@coroutineScope cachedResults.mapValues { it.value.apiPackage } + return@coroutineScope validResults.mapValues { it.value.apiPackage } } val onlineResults = defaultRequest<_, List>( @@ -227,9 +223,13 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ).associateBy { if (lookupField == "id") it.id else it.idHash } + + onlineResults.values.forEach { + val cacheKey = if (lookupField == "id") it.id else it.idHash - packageInfoCache.insert( + + apiPackageCacheDB.insert( ApiPackageCacheEntry( _id = cachedResults[cacheKey]?._id, it @@ -245,7 +245,7 @@ public class PackageSearchApiClient( request: SearchPackagesRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): List { - val searchCache = cachedb.searchPackageCache() + val searchCache = cacheDB.searchPackageCache() val isOffline = !onlineStateFlow.value val searchResult = searchCache.find("searchQuery", request.searchQuery) @@ -273,7 +273,7 @@ public class PackageSearchApiClient( request: SearchPackagesStartScrollRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): SearchPackagesScrollResponse { - val cache = cachedb.scrollStartPackageCache() + val cache = cacheDB.scrollStartPackageCache() val cacheResult = cache .find("searchQuery", request.searchQuery) @@ -313,7 +313,7 @@ public class PackageSearchApiClient( request: SearchProjectRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)?, ): List { - val apiProjectsCache = cachedb.apiProjectsCache() + val apiProjectsCache = cacheDB.apiProjectsCache() val cacheResult = apiProjectsCache.find("queryString", request.query) .firstOrNull() @@ -325,7 +325,6 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { - apiProjectsCache.insert( ApiProjectsCacheEntry( _id = cacheResult?._id, @@ -337,17 +336,46 @@ public class PackageSearchApiClient( } - public suspend fun refreshPackagesInfo( request: RefreshPackagesInfoRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, - ): List = - defaultRequest<_, List>( - method = HttpMethod.Post, - url = endpoints.refreshPackagesInfo, - body = request, - requestBuilder = requestBuilder, - ) + ): List { + //no caches for this endpoint + val results = + defaultRequest<_, List>( + method = HttpMethod.Post, + url = endpoints.refreshPackagesInfo, + body = request, + requestBuilder = requestBuilder, + ) + + // update ApiPackageCache + coroutineScope.launch(Dispatchers.IO) { + val apiPackageCacheDB = cacheDB.apiPackagesCache() + val updates = + results + .map { result -> + async { + apiPackageCacheDB + .find("id", result.id) + .firstOrNull() + ?.let { it._id to result } // Pair the ID with the cache entry for easier processing + } + } + .awaitAll() + .filterNotNull() + .toMap() + updates.forEach { + apiPackageCacheDB.insert(ApiPackageCacheEntry(it.key, it.value)) + } + val updatedIDS = updates.map { it.value.id } + results.filter { it.id !in updatedIDS }.forEach { + apiPackageCacheDB.insert(ApiPackageCacheEntry(apiPackage = it)) + } + } + return results + + } private suspend fun checkOnlineState(): Boolean { val request = @@ -363,6 +391,15 @@ public class PackageSearchApiClient( return isOnline } + private fun initiateOnlineCheckJob(onlineCheckInterval: Duration) { + coroutineScope.launch(Dispatchers.IO) { + while (coroutineScope.isActive) { + _onlineStateFlow.emit(checkOnlineState()) + delay(onlineCheckInterval) + } + } + } + } diff --git a/http/client/src/commonTest/kotlin/CacheTests.kt b/http/client/src/commonTest/kotlin/CacheTests.kt new file mode 100644 index 0000000..9880ab5 --- /dev/null +++ b/http/client/src/commonTest/kotlin/CacheTests.kt @@ -0,0 +1,132 @@ +import cache.ApiPackageCacheEntry +import cache.CacheDB +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.logging.DEFAULT +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headers +import io.ktor.serialization.kotlinx.json.json +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Clock +import kotlinx.document.database.mvstore.asDataStore +import kotlinx.serialization.json.Json +import org.h2.mvstore.MVStore +import org.jetbrains.packagesearch.api.v3.ApiPackage +import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient +import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints +import org.junit.jupiter.api.Test +import kotlin.collections.first +import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +class CacheTests { + + + @Test + fun testCacheHit() = runTest(timeout = 10.seconds) { + + val jsonResponse = this::class.java.classLoader.getResource("package-info-by-ids-ktor.json")!!.readText() + val mockResponse = Json.decodeFromString>(jsonResponse).first() + + val mockEngine = buildMockEngine(jsonResponse) + + // ApiClient coroutine scope will be created from httpClient coroutine context + val apiClient = PackageSearchApiClient( + httpClient = setupHttpClient(mockEngine), + endpoints = PackageSearchEndpoints.DEV, + dataStore = MVStore.open(null).asDataStore(), //in memory db + ) + + // First request (populates cache) + val response1 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) + assert(response1.values.firstOrNull()!!.id == mockResponse.id) + + // Second request (should hit cache) + val response2 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) + assert(response2.values.firstOrNull()!!.id == mockResponse.id) + + // Ensure the mock engine wasn't called a second time. + val endpointsCalls = + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds) + + mockEngine.close() + assertEquals(1, endpointsCalls) + } + + + @Test + fun testCacheRefresh() = runTest(timeout = 20.seconds) { + val jsonResponse = this::class.java.classLoader.getResource("package-info-by-ids-ktor.json")!!.readText() + val mockResponse = Json.decodeFromString>(jsonResponse).first() + + val expiredDBEntry = ApiPackageCacheEntry( + apiPackage = mockResponse, + expires = Clock.System.now().minus(1.minutes), + ) + + val dataStore = MVStore.open(null).asDataStore() + //setting up db + val collection = CacheDB(dataStore).apiPackagesCache() + collection.insert(expiredDBEntry) + + val mockEngine = buildMockEngine(jsonResponse) + + // ApiClient coroutine scope will be created from httpClient coroutine context + val apiClient = PackageSearchApiClient( + httpClient = setupHttpClient(mockEngine), + endpoints = PackageSearchEndpoints.DEV, + dataStore = dataStore, + ) + + // First request (should refresh cache) + val response1 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) + assert(response1.values.firstOrNull()!!.id == mockResponse.id) + + // Ensure data has been retrieved from BE. + assertEquals(1, mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) + + + // Second request (should hit cache) + val response2 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) + assert(response2.values.firstOrNull()!!.id == mockResponse.id) + + mockEngine.close() + assertEquals(1, mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) + + } + + private fun setupHttpClient(mockEngine: MockEngine): HttpClient = HttpClient(mockEngine) { + install(ContentNegotiation) { + json() + } + install(Logging) { + logger = Logger.DEFAULT + level = LogLevel.HEADERS + } + } + + private fun buildMockEngine(jsonResponse: String): MockEngine = MockEngine { + respond( + content = jsonResponse, + status = HttpStatusCode.OK, + headers = headers { + append(HttpHeaders.ContentType, "application/json") + } + + ) + } + + +} + + + + + diff --git a/http/client/src/commonTest/kotlin/Utils.kt b/http/client/src/commonTest/kotlin/Utils.kt new file mode 100644 index 0000000..669eefa --- /dev/null +++ b/http/client/src/commonTest/kotlin/Utils.kt @@ -0,0 +1,6 @@ +import io.ktor.client.engine.mock.MockEngine +import io.ktor.http.Url + + +fun MockEngine.getRequestCount(filterUrl: Url) = + requestHistory.filter { it.url == filterUrl }.size diff --git a/http/client/src/commonTest/resources/package-info-by-ids-ktor.json b/http/client/src/commonTest/resources/package-info-by-ids-ktor.json new file mode 100644 index 0000000..1222704 --- /dev/null +++ b/http/client/src/commonTest/resources/package-info-by-ids-ktor.json @@ -0,0 +1,15073 @@ +[ + { + "type": "maven", + "id": "maven:io.ktor:ktor-client-core", + "idHash": "6d2eceeb52da1da9817f4fb83d0c8a98173960f3690eb65206b606ad14b2f1d1", + "rankingMetric": 0.5113194418722041, + "versions": { + "latestStable": { + "type": "gradleVersion", + "normalized": { + "type": "semantic", + "versionName": "2.3.11", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.11", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ], + "vulnerability": { + "isVulnerable": false, + "issues": null + }, + "dependencies": [ + { + "groupId": "org.jetbrains.kotlinx", + "artifactId": "kotlinx-coroutines-core", + "version": "1.7.1", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-http", + "version": "2.3.11", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-events", + "version": "2.3.11", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-websocket-serialization", + "version": "2.3.11", + "scope": "runtime" + }, + { + "groupId": "org.jetbrains.kotlin", + "artifactId": "kotlin-stdlib-common", + "version": "1.8.22", + "scope": "runtime" + }, + { + "groupId": "org.jetbrains.kotlinx", + "artifactId": "atomicfu", + "version": "0.19.0", + "scope": "runtime" + } + ], + "artifacts": [ + { + "name": "ktor-client-core-2.3.11-kotlin-tooling-metadata.json", + "md5": "33481d030d7ab3acaa041c6d343935c5", + "sha1": "951d8aa56d8938e65ade0c3c5d5d3c164293ef6d", + "sha256": "babe8d852f311fb3d5a1b3c52597089f2433e735646b0b0fa4ea06afcae40736", + "sha512": "ac5064b3e5bac864feea11fb0b1feca20129832cc92982e4d798439f12d4f12f4f427c8d056499d638b1d964a26c23f4adbf6d64e24964583615e7f8681f3273" + }, + { + "name": "ktor-client-core-2.3.11-sources.jar", + "md5": "8015b0f681fa13694f3d999b7b56b045", + "sha1": "e395ae26f73172f8f0957624f857e704fc34bfa0", + "sha256": "1535d4e2e0f3e72364d2e49b31519a0f11c9298e2e55b4a78514c8fe28ae9587", + "sha512": null + }, + { + "name": "ktor-client-core-2.3.11.jar", + "md5": "1ac8c8b8e3e20de8dd4e1b83c838a11a", + "sha1": "b4f75ab2bce9d3788efdeb557a39611f072f7a4b", + "sha256": "13f9d5ec238aa8da17f2c080500d068a963a38a31a16415097b840e5f8c3bff6", + "sha512": "37af55d9adba953c6e09988a0fe0d9afbb8930efb52c0c011aaea062e4c1594a60e14d64ebafa96bf6f1c0478db8021002a08e69a7517ef1a884bc8179811562" + }, + { + "name": "ktor-client-core-2.3.11.module", + "md5": "f7d7fb9816aa4d205148e7078459b36c", + "sha1": "2599e1c47483f3092dc620350232a711eddaee9a", + "sha256": "127d461a2d17da56605c27f2d266d25317dfac558cdbb230dd17526685d032d6", + "sha512": "20ca7caa5f3cf6e32bd6799a355627e22ba401d59a4aed94dc5919ec84304a1e159fd85dca5d645a6edd1b938a4a6c91f85586b3650a5cb19171044c8eee7530" + }, + { + "name": "ktor-client-core-2.3.11.pom", + "md5": "96a019035c9220e57f2fb1f63af7eaad", + "sha1": "2bcecf049f58dc75158a3354f916b4787b797f21", + "sha256": "140647469a3b0e9aa399e54199084e64f1d8a608e13ba7ba7cc535acf1681857", + "sha512": "73d7c0663e212f58072757d65fa64e9fa587ecb7426481508952ff2e4b4aebff5b62f076b9ee14e79e47c1d9d4a27c6c59bdadf0db32355d264019c2771151ea" + } + ], + "name": "ktor-client-core", + "description": "Ktor is a framework for quickly creating web applications in Kotlin with minimal effort.", + "authors": [ + { + "name": "JetBrains Team", + "email": "", + "org": "JetBrains", + "orgUrl": null + } + ], + "scmUrl": "https://github.com/ktorio/ktor.git", + "variants": [ + { + "type": "withFiles", + "name": "metadataApiElements", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "common" + } + }, + "dependencies": [ + { + "group": "org.jetbrains.kotlinx", + "module": "kotlinx-coroutines-core", + "version": "1.7.1" + }, + { + "group": "io.ktor", + "module": "ktor-http", + "version": "2.3.11" + }, + { + "group": "io.ktor", + "module": "ktor-events", + "version": "2.3.11" + }, + { + "group": "io.ktor", + "module": "ktor-websocket-serialization", + "version": "2.3.11" + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-common", + "version": "1.8.22" + }, + { + "group": "org.jetbrains.kotlinx", + "module": "atomicfu", + "version": "0.19.0" + } + ], + "files": [ + { + "name": "ktor-client-core-metadata-2.3.11.jar", + "url": "ktor-client-core-2.3.11.jar", + "size": 58970, + "sha1": "b4f75ab2bce9d3788efdeb557a39611f072f7a4b", + "md5": "1ac8c8b8e3e20de8dd4e1b83c838a11a", + "sha256": "13f9d5ec238aa8da17f2c080500d068a963a38a31a16415097b840e5f8c3bff6", + "sha512": "37af55d9adba953c6e09988a0fe0d9afbb8930efb52c0c011aaea062e4c1594a60e14d64ebafa96bf6f1c0478db8021002a08e69a7517ef1a884bc8179811562" + } + ] + }, + { + "type": "withFiles", + "name": "metadataSourcesElements", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "common" + } + }, + "dependencies": [], + "files": [ + { + "name": "ktor-client-core-kotlin-2.3.11-sources.jar", + "url": "ktor-client-core-2.3.11-sources.jar", + "size": 103559, + "sha1": "e395ae26f73172f8f0957624f857e704fc34bfa0", + "md5": "8015b0f681fa13694f3d999b7b56b045", + "sha256": "1535d4e2e0f3e72364d2e49b31519a0f11c9298e2e55b4a78514c8fe28ae9587", + "sha512": "198f29ed507f21b729dd2a7996fc11e2250a775ebad504f450014b6a51ea21992784162ad7cb6e816e93cfc26bd66d09fa92d80162e700f995637c2191a02f02" + } + ] + }, + { + "type": "withAvailableAt", + "name": "iosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosarm64/2.3.11/ktor-client-core-iosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosarm64/2.3.11/ktor-client-core-iosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosarm64/2.3.11/ktor-client-core-iosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosSimulatorArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iossimulatorarm64/2.3.11/ktor-client-core-iossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosSimulatorArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iossimulatorarm64/2.3.11/ktor-client-core-iossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosSimulatorArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iossimulatorarm64/2.3.11/ktor-client-core-iossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosx64/2.3.11/ktor-client-core-iosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosx64/2.3.11/ktor-client-core-iosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "iosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosx64/2.3.11/ktor-client-core-iosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-iosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "jsApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.js.compiler": { + "type": "exactMatch", + "value": "ir" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "js" + } + }, + "available-at": { + "url": "../../ktor-client-core-js/2.3.11/ktor-client-core-js-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-js", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "jsRuntimeElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.js.compiler": { + "type": "exactMatch", + "value": "ir" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "js" + } + }, + "available-at": { + "url": "../../ktor-client-core-js/2.3.11/ktor-client-core-js-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-js", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "jsSourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.js.compiler": { + "type": "exactMatch", + "value": "ir" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "js" + } + }, + "available-at": { + "url": "../../ktor-client-core-js/2.3.11/ktor-client-core-js-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-js", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "jvmApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.libraryelements": { + "type": "exactMatch", + "value": "jar" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "java-api" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "jvm" + } + }, + "available-at": { + "url": "../../ktor-client-core-jvm/2.3.11/ktor-client-core-jvm-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-jvm", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "jvmRuntimeElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.libraryelements": { + "type": "exactMatch", + "value": "jar" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "java-runtime" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "jvm" + } + }, + "available-at": { + "url": "../../ktor-client-core-jvm/2.3.11/ktor-client-core-jvm-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-jvm", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "jvmSourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.libraryelements": { + "type": "exactMatch", + "value": "jar" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "java-runtime" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "jvm" + } + }, + "available-at": { + "url": "../../ktor-client-core-jvm/2.3.11/ktor-client-core-jvm-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-jvm", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "linuxArm64ApiElements-published", + "attributes": { + "artifactType": { + "type": "exactMatch", + "value": "org.jetbrains.kotlin.klib" + }, + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxarm64/2.3.11/ktor-client-core-linuxarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "linuxArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxarm64/2.3.11/ktor-client-core-linuxarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "linuxX64ApiElements-published", + "attributes": { + "artifactType": { + "type": "exactMatch", + "value": "org.jetbrains.kotlin.klib" + }, + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxx64/2.3.11/ktor-client-core-linuxx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "linuxX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxx64/2.3.11/ktor-client-core-linuxx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "macosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosarm64/2.3.11/ktor-client-core-macosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-macosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "macosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosarm64/2.3.11/ktor-client-core-macosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-macosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "macosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosarm64/2.3.11/ktor-client-core-macosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-macosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "macosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosx64/2.3.11/ktor-client-core-macosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-macosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "macosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosx64/2.3.11/ktor-client-core-macosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-macosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "macosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosx64/2.3.11/ktor-client-core-macosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-macosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "mingwX64ApiElements-published", + "attributes": { + "artifactType": { + "type": "exactMatch", + "value": "org.jetbrains.kotlin.klib" + }, + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "mingw_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-mingwx64/2.3.11/ktor-client-core-mingwx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-mingwx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "mingwX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "mingw_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-mingwx64/2.3.11/ktor-client-core-mingwx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-mingwx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosarm64/2.3.11/ktor-client-core-tvosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosarm64/2.3.11/ktor-client-core-tvosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosarm64/2.3.11/ktor-client-core-tvosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosSimulatorArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvossimulatorarm64/2.3.11/ktor-client-core-tvossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosSimulatorArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvossimulatorarm64/2.3.11/ktor-client-core-tvossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosSimulatorArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvossimulatorarm64/2.3.11/ktor-client-core-tvossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosx64/2.3.11/ktor-client-core-tvosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosx64/2.3.11/ktor-client-core-tvosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "tvosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosx64/2.3.11/ktor-client-core-tvosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm32ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm32" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm32/2.3.11/ktor-client-core-watchosarm32-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm32", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm32MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm32" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm32/2.3.11/ktor-client-core-watchosarm32-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm32", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm32SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm32" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm32/2.3.11/ktor-client-core-watchosarm32-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm32", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm64/2.3.11/ktor-client-core-watchosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm64/2.3.11/ktor-client-core-watchosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm64/2.3.11/ktor-client-core-watchosarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosSimulatorArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchossimulatorarm64/2.3.11/ktor-client-core-watchossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosSimulatorArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchossimulatorarm64/2.3.11/ktor-client-core-watchossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosSimulatorArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchossimulatorarm64/2.3.11/ktor-client-core-watchossimulatorarm64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchossimulatorarm64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosx64/2.3.11/ktor-client-core-watchosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosx64/2.3.11/ktor-client-core-watchosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosx64", + "version": "2.3.11" + } + }, + { + "type": "withAvailableAt", + "name": "watchosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosx64/2.3.11/ktor-client-core-watchosx64-2.3.11.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosx64", + "version": "2.3.11" + } + } + ], + "parentComponent": null + }, + "latest": { + "type": "gradleVersion", + "normalized": { + "type": "semantic", + "versionName": "3.0.0-beta-2-eap-884", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-2-eap-884", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ], + "vulnerability": { + "isVulnerable": false, + "issues": null + }, + "dependencies": [ + { + "groupId": "org.jetbrains.kotlinx", + "artifactId": "kotlinx-coroutines-core", + "version": "1.7.3", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-http", + "version": "3.0.0-beta-2-eap-884", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-events", + "version": "3.0.0-beta-2-eap-884", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-websocket-serialization", + "version": "3.0.0-beta-2-eap-884", + "scope": "runtime" + }, + { + "groupId": "io.ktor", + "artifactId": "ktor-sse", + "version": "3.0.0-beta-2-eap-884", + "scope": "runtime" + }, + { + "groupId": "org.jetbrains.kotlin", + "artifactId": "kotlin-stdlib", + "version": "1.9.22", + "scope": "runtime" + }, + { + "groupId": "org.jetbrains.kotlinx", + "artifactId": "atomicfu", + "version": "0.22.0", + "scope": "runtime" + } + ], + "artifacts": [ + { + "name": "ktor-client-core-3.0.0-beta-2-eap-884.jar", + "md5": "c8f4cff765f2e0219b43e0ba6198eaa2", + "sha1": "c63d2b2f8868f53232e9ff6e6f77199f04136a3a", + "sha256": "a5d88ff2494bf6800996784bd56e526eb7ea670cc0f3996f458b4e96b6b58224", + "sha512": "fa3d0d4b243ce4b623d350ce9f6ffc8e98d7df03e1e7382380e871d5e965f88d33aee082302de121b994fc03d091c26512d88aa9218920243d5d41946e9c9efa" + }, + { + "name": "ktor-client-core-3.0.0-beta-2-eap-884-kotlin-tooling-metadata.json", + "md5": "5a05f6185a79ffb4ab5fef58399520cb", + "sha1": "0efdcca5c49371f4444ac9350ccff37cdbc9b1ce", + "sha256": "427b5fef9eac508b986840e931d7c40dc357dc5898e15703aa9e6402f890cbc6", + "sha512": "0f3fe82270626e39f6300c9ee5f798c9e7754b51d9b8b3a41beeaccc738475842d06fa72b2106448c38b35063b8fb84658f7a9426df14fff9114879ea77ac5c5" + }, + { + "name": "ktor-client-core-3.0.0-beta-2-eap-884.module", + "md5": "21f5effd011579f3acaca08b095f6d3d", + "sha1": "0f1a5ba396e070f39e72fe7c62ef69a2b74834b0", + "sha256": "4777bb786ad8f2a388385fc3b1151c59bf223425e8b0bc3b69dab8b65a4935db", + "sha512": "2041433b2cd7a6c4f58d45fc6ec560082a192509953d30e9a2a7ca57027a21e9bf234b61404dd104c5260c3792a2b8e86d6ef7e7a1ecb464c2cc664f198b2c63" + }, + { + "name": "ktor-client-core-3.0.0-beta-2-eap-884.pom", + "md5": "01217b6de7cd416a4fbd110dd45bcf68", + "sha1": "0e24659562e5d920939c567428c81c710e183d42", + "sha256": "75d45bf22f95f9f8e65b4c00757dfc8784552e970232e72dbb8f5be9784279d2", + "sha512": "bfe54458ba85817c9c724b903e861dc146e3cb2afdfe1819cb8f17019e98d53224b27b022281c79a41a4821d92a99876022ed1a4e53d26cc08ff7cf31d7cab8d" + }, + { + "name": "ktor-client-core-3.0.0-beta-2-eap-884-sources.jar", + "md5": "7f4bb28b3c597c08a592b498643827ab", + "sha1": "f19706aa73c5b80e629db7898b2944adcf7df24e", + "sha256": "27f7f1a2e737f1086ccd04655ae4607074460da2ecb4c5b242941edadfde99c0", + "sha512": "56b232a9380d0497209bf787506d430da9313f18a593bb663baed6eae0fa09530e5528c02d108cebac20e05e06c1a93bdc9f472932fcef0c5059e2d6f7968d28" + } + ], + "name": "ktor-client-core", + "description": "Ktor is a framework for quickly creating web applications in Kotlin with minimal effort.", + "authors": [ + { + "name": "JetBrains Team", + "email": "", + "org": "JetBrains", + "orgUrl": null + } + ], + "scmUrl": "https://github.com/ktorio/ktor.git", + "variants": [ + { + "type": "withFiles", + "name": "metadataApiElements", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "common" + } + }, + "dependencies": [ + { + "group": "org.jetbrains.kotlinx", + "module": "kotlinx-coroutines-core", + "version": "1.7.3" + }, + { + "group": "io.ktor", + "module": "ktor-http", + "version": "3.0.0-beta-2-eap-884" + }, + { + "group": "io.ktor", + "module": "ktor-events", + "version": "3.0.0-beta-2-eap-884" + }, + { + "group": "io.ktor", + "module": "ktor-websocket-serialization", + "version": "3.0.0-beta-2-eap-884" + }, + { + "group": "io.ktor", + "module": "ktor-sse", + "version": "3.0.0-beta-2-eap-884" + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib", + "version": "1.9.22" + }, + { + "group": "org.jetbrains.kotlinx", + "module": "atomicfu", + "version": "0.22.0" + } + ], + "files": [ + { + "name": "ktor-client-core-metadata-3.0.0-beta-2-eap-884.jar", + "url": "ktor-client-core-3.0.0-beta-2-eap-884.jar", + "size": 60680, + "sha1": "c63d2b2f8868f53232e9ff6e6f77199f04136a3a", + "md5": "c8f4cff765f2e0219b43e0ba6198eaa2", + "sha256": "a5d88ff2494bf6800996784bd56e526eb7ea670cc0f3996f458b4e96b6b58224", + "sha512": "fa3d0d4b243ce4b623d350ce9f6ffc8e98d7df03e1e7382380e871d5e965f88d33aee082302de121b994fc03d091c26512d88aa9218920243d5d41946e9c9efa" + } + ] + }, + { + "type": "withFiles", + "name": "metadataSourcesElements", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "common" + } + }, + "dependencies": [], + "files": [ + { + "name": "ktor-client-core-kotlin-3.0.0-beta-2-eap-884-sources.jar", + "url": "ktor-client-core-3.0.0-beta-2-eap-884-sources.jar", + "size": 108844, + "sha1": "f19706aa73c5b80e629db7898b2944adcf7df24e", + "md5": "7f4bb28b3c597c08a592b498643827ab", + "sha256": "27f7f1a2e737f1086ccd04655ae4607074460da2ecb4c5b242941edadfde99c0", + "sha512": "56b232a9380d0497209bf787506d430da9313f18a593bb663baed6eae0fa09530e5528c02d108cebac20e05e06c1a93bdc9f472932fcef0c5059e2d6f7968d28" + } + ] + }, + { + "type": "withAvailableAt", + "name": "iosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosarm64/3.0.0-beta-2-eap-884/ktor-client-core-iosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosarm64/3.0.0-beta-2-eap-884/ktor-client-core-iosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosarm64/3.0.0-beta-2-eap-884/ktor-client-core-iosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosSimulatorArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-iossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosSimulatorArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-iossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosSimulatorArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-iossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosx64/3.0.0-beta-2-eap-884/ktor-client-core-iosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosx64/3.0.0-beta-2-eap-884/ktor-client-core-iosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "iosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "ios_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-iosx64/3.0.0-beta-2-eap-884/ktor-client-core-iosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-iosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "jsApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.js.compiler": { + "type": "exactMatch", + "value": "ir" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "js" + } + }, + "available-at": { + "url": "../../ktor-client-core-js/3.0.0-beta-2-eap-884/ktor-client-core-js-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-js", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "jsRuntimeElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.js.compiler": { + "type": "exactMatch", + "value": "ir" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "js" + } + }, + "available-at": { + "url": "../../ktor-client-core-js/3.0.0-beta-2-eap-884/ktor-client-core-js-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-js", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "jsSourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.js.compiler": { + "type": "exactMatch", + "value": "ir" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "js" + } + }, + "available-at": { + "url": "../../ktor-client-core-js/3.0.0-beta-2-eap-884/ktor-client-core-js-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-js", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "jvmApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.libraryelements": { + "type": "exactMatch", + "value": "jar" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "java-api" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "jvm" + } + }, + "available-at": { + "url": "../../ktor-client-core-jvm/3.0.0-beta-2-eap-884/ktor-client-core-jvm-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-jvm", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "jvmRuntimeElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.libraryelements": { + "type": "exactMatch", + "value": "jar" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "java-runtime" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "jvm" + } + }, + "available-at": { + "url": "../../ktor-client-core-jvm/3.0.0-beta-2-eap-884/ktor-client-core-jvm-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-jvm", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "jvmSourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.libraryelements": { + "type": "exactMatch", + "value": "jar" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "java-runtime" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "jvm" + } + }, + "available-at": { + "url": "../../ktor-client-core-jvm/3.0.0-beta-2-eap-884/ktor-client-core-jvm-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-jvm", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "linuxArm64ApiElements-published", + "attributes": { + "artifactType": { + "type": "exactMatch", + "value": "org.jetbrains.kotlin.klib" + }, + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxarm64/3.0.0-beta-2-eap-884/ktor-client-core-linuxarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "linuxArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxarm64/3.0.0-beta-2-eap-884/ktor-client-core-linuxarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "linuxX64ApiElements-published", + "attributes": { + "artifactType": { + "type": "exactMatch", + "value": "org.jetbrains.kotlin.klib" + }, + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxx64/3.0.0-beta-2-eap-884/ktor-client-core-linuxx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "linuxX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "linux_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-linuxx64/3.0.0-beta-2-eap-884/ktor-client-core-linuxx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-linuxx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "macosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosarm64/3.0.0-beta-2-eap-884/ktor-client-core-macosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-macosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "macosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosarm64/3.0.0-beta-2-eap-884/ktor-client-core-macosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-macosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "macosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosarm64/3.0.0-beta-2-eap-884/ktor-client-core-macosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-macosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "macosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosx64/3.0.0-beta-2-eap-884/ktor-client-core-macosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-macosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "macosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosx64/3.0.0-beta-2-eap-884/ktor-client-core-macosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-macosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "macosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "macos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-macosx64/3.0.0-beta-2-eap-884/ktor-client-core-macosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-macosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "mingwX64ApiElements-published", + "attributes": { + "artifactType": { + "type": "exactMatch", + "value": "org.jetbrains.kotlin.klib" + }, + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "mingw_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-mingwx64/3.0.0-beta-2-eap-884/ktor-client-core-mingwx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-mingwx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "mingwX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "mingw_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-mingwx64/3.0.0-beta-2-eap-884/ktor-client-core-mingwx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-mingwx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosarm64/3.0.0-beta-2-eap-884/ktor-client-core-tvosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosarm64/3.0.0-beta-2-eap-884/ktor-client-core-tvosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosarm64/3.0.0-beta-2-eap-884/ktor-client-core-tvosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosSimulatorArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-tvossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosSimulatorArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-tvossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosSimulatorArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-tvossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosx64/3.0.0-beta-2-eap-884/ktor-client-core-tvosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosx64/3.0.0-beta-2-eap-884/ktor-client-core-tvosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "tvosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "tvos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-tvosx64/3.0.0-beta-2-eap-884/ktor-client-core-tvosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-tvosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm32ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm32" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm32/3.0.0-beta-2-eap-884/ktor-client-core-watchosarm32-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm32", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm32SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm32" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm32/3.0.0-beta-2-eap-884/ktor-client-core-watchosarm32-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm32", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm32MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm32" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm32/3.0.0-beta-2-eap-884/ktor-client-core-watchosarm32-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm32", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm64/3.0.0-beta-2-eap-884/ktor-client-core-watchosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm64/3.0.0-beta-2-eap-884/ktor-client-core-watchosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosarm64/3.0.0-beta-2-eap-884/ktor-client-core-watchosarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosSimulatorArm64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-watchossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosSimulatorArm64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-watchossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosSimulatorArm64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_simulator_arm64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchossimulatorarm64/3.0.0-beta-2-eap-884/ktor-client-core-watchossimulatorarm64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchossimulatorarm64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosX64ApiElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-api" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosx64/3.0.0-beta-2-eap-884/ktor-client-core-watchosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosX64SourcesElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "documentation" + }, + "org.gradle.dependency.bundling": { + "type": "exactMatch", + "value": "external" + }, + "org.gradle.docstype": { + "type": "exactMatch", + "value": "sources" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-runtime" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosx64/3.0.0-beta-2-eap-884/ktor-client-core-watchosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosx64", + "version": "3.0.0-beta-2-eap-884" + } + }, + { + "type": "withAvailableAt", + "name": "watchosX64MetadataElements-published", + "attributes": { + "org.gradle.category": { + "type": "exactMatch", + "value": "library" + }, + "org.gradle.usage": { + "type": "exactMatch", + "value": "kotlin-metadata" + }, + "org.jetbrains.kotlin.native.target": { + "type": "exactMatch", + "value": "watchos_x64" + }, + "org.jetbrains.kotlin.platform.type": { + "type": "exactMatch", + "value": "native" + } + }, + "available-at": { + "url": "../../ktor-client-core-watchosx64/3.0.0-beta-2-eap-884/ktor-client-core-watchosx64-3.0.0-beta-2-eap-884.module", + "group": "io.ktor", + "module": "ktor-client-core-watchosx64", + "version": "3.0.0-beta-2-eap-884" + } + } + ], + "parentComponent": null + }, + "all": [ + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-3", + "isStable": false, + "releasedAt": "2021-01-29T10:46:08Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-3", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-5", + "isStable": false, + "releasedAt": "2021-02-04T12:03:41Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-5", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-6", + "isStable": false, + "releasedAt": "2021-02-04T14:10:42Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-6", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-17", + "isStable": false, + "releasedAt": "2021-02-05T11:33:28Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-19", + "isStable": false, + "releasedAt": "2021-02-05T12:11:52Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-22", + "isStable": false, + "releasedAt": "2021-02-05T15:35:41Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-22", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-23", + "isStable": false, + "releasedAt": "2021-02-10T12:00:55Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-25", + "isStable": false, + "releasedAt": "2021-02-11T20:10:41Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-25", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-24", + "isStable": false, + "releasedAt": "2021-02-11T20:38:43Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-26", + "isStable": false, + "releasedAt": "2021-02-12T20:36:30Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-27", + "isStable": false, + "releasedAt": "2021-02-15T20:15:40Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-28", + "isStable": false, + "releasedAt": "2021-02-16T20:47:39Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-30", + "isStable": false, + "releasedAt": "2021-02-17T20:15:42Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-31", + "isStable": false, + "releasedAt": "2021-02-19T20:48:36Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-36", + "isStable": false, + "releasedAt": "2021-02-26T20:17:56Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-36", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-35", + "isStable": false, + "releasedAt": "2021-02-26T20:48:05Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-37", + "isStable": false, + "releasedAt": "2021-03-01T20:41:19Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-37", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-38", + "isStable": false, + "releasedAt": "2021-03-02T20:47:48Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-39", + "isStable": false, + "releasedAt": "2021-03-05T20:16:07Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-40", + "isStable": false, + "releasedAt": "2021-03-09T20:17:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-41", + "isStable": false, + "releasedAt": "2021-03-10T20:17:23Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-42", + "isStable": false, + "releasedAt": "2021-03-10T20:31:50Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-42", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-43", + "isStable": false, + "releasedAt": "2021-03-12T20:17:48Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-44", + "isStable": false, + "releasedAt": "2021-03-15T20:48:39Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-45", + "isStable": false, + "releasedAt": "2021-03-16T20:48:59Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-46", + "isStable": false, + "releasedAt": "2021-03-17T20:48:41Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-46", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-49", + "isStable": false, + "releasedAt": "2021-03-22T20:08:00Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-52", + "isStable": false, + "releasedAt": "2021-03-23T13:37:55Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-54", + "isStable": false, + "releasedAt": "2021-03-23T20:40:53Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-54", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-53", + "isStable": false, + "releasedAt": "2021-03-23T20:57:18Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-55", + "isStable": false, + "releasedAt": "2021-03-23T21:11:14Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-55", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-56", + "isStable": false, + "releasedAt": "2021-03-25T20:10:24Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-57", + "isStable": false, + "releasedAt": "2021-03-28T20:16:11Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-57", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-58", + "isStable": false, + "releasedAt": "2021-03-29T20:15:08Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-58", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-59", + "isStable": false, + "releasedAt": "2021-03-30T13:48:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-60", + "isStable": false, + "releasedAt": "2021-03-30T20:41:24Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-63", + "isStable": false, + "releasedAt": "2021-03-30T21:01:25Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-64", + "isStable": false, + "releasedAt": "2021-03-31T17:17:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-65", + "isStable": false, + "releasedAt": "2021-03-31T20:08:42Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-66", + "isStable": false, + "releasedAt": "2021-03-31T20:26:16Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-66", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-67", + "isStable": false, + "releasedAt": "2021-04-01T13:08:15Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "new-routing-eap-69", + "isStable": false, + "releasedAt": "2021-04-01T16:27:02Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-70", + "isStable": false, + "releasedAt": "2021-04-02T20:49:38Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-71", + "isStable": false, + "releasedAt": "2021-04-02T21:06:04Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-71", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-72", + "isStable": false, + "releasedAt": "2021-04-06T21:39:41Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-72", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-73", + "isStable": false, + "releasedAt": "2021-04-07T20:39:54Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-75", + "isStable": false, + "releasedAt": "2021-04-08T20:56:43Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-75", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-76", + "isStable": false, + "releasedAt": "2021-04-08T21:11:33Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-76", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-77", + "isStable": false, + "releasedAt": "2021-04-09T14:45:53Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-77" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "master-78", + "isStable": false, + "releasedAt": "2021-04-09T20:45:21Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-79", + "isStable": false, + "releasedAt": "2021-04-09T20:59:48Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-79", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-80", + "isStable": false, + "releasedAt": "2021-04-09T21:14:04Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-80", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-81", + "isStable": false, + "releasedAt": "2021-04-12T20:16:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-82", + "isStable": false, + "releasedAt": "2021-04-12T21:30:37Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-82", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-83", + "isStable": false, + "releasedAt": "2021-04-12T21:44:56Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-83", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-84", + "isStable": false, + "releasedAt": "2021-04-13T20:45:50Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-85", + "isStable": false, + "releasedAt": "2021-04-14T20:45:57Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-85", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-86", + "isStable": false, + "releasedAt": "2021-04-15T20:47:48Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-86", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-88", + "isStable": false, + "releasedAt": "2021-04-16T20:46:36Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-88" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-89", + "isStable": false, + "releasedAt": "2021-04-20T20:46:17Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-90", + "isStable": false, + "releasedAt": "2021-04-20T21:01:35Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-90" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-91", + "isStable": false, + "releasedAt": "2021-04-20T21:17:58Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-91", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-93", + "isStable": false, + "releasedAt": "2021-04-21T20:18:53Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-94", + "isStable": false, + "releasedAt": "2021-04-21T20:47:42Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-94" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-95", + "isStable": false, + "releasedAt": "2021-04-22T20:47:17Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-96", + "isStable": false, + "releasedAt": "2021-04-22T21:03:00Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-96", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-97", + "isStable": false, + "releasedAt": "2021-04-22T21:17:39Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-97" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-99", + "isStable": false, + "releasedAt": "2021-04-23T20:44:53Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-100", + "isStable": false, + "releasedAt": "2021-04-26T20:16:22Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-101", + "isStable": false, + "releasedAt": "2021-04-26T20:48:43Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-101", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-102", + "isStable": false, + "releasedAt": "2021-04-27T21:09:39Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-102", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-104", + "isStable": false, + "releasedAt": "2021-04-28T20:45:40Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-104", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-103", + "isStable": false, + "releasedAt": "2021-04-28T21:01:45Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-103", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-105", + "isStable": false, + "releasedAt": "2021-04-29T20:12:03Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-106", + "isStable": false, + "releasedAt": "2021-04-29T20:27:28Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-106", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-107", + "isStable": false, + "releasedAt": "2021-04-29T20:42:18Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-107", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-108", + "isStable": false, + "releasedAt": "2021-04-29T20:56:21Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-108" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-111", + "isStable": false, + "releasedAt": "2021-04-30T20:10:23Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-111", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-109", + "isStable": false, + "releasedAt": "2021-04-30T20:30:36Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-eap-110", + "isStable": false, + "releasedAt": "2021-04-30T20:46:43Z", + "semanticPart": "1.6.0", + "stabilityMarker": "-eap-110", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-112", + "isStable": false, + "releasedAt": "2021-04-30T21:00:59Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-112" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-113", + "isStable": false, + "releasedAt": "2021-05-03T20:50:18Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-114", + "isStable": false, + "releasedAt": "2021-05-04T20:18:43Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-115", + "isStable": false, + "releasedAt": "2021-05-04T20:45:16Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-115" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-116", + "isStable": false, + "releasedAt": "2021-05-05T20:48:44Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-116", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-117", + "isStable": false, + "releasedAt": "2021-05-06T20:44:47Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-118", + "isStable": false, + "releasedAt": "2021-05-11T20:15:50Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-119", + "isStable": false, + "releasedAt": "2021-05-11T20:31:47Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-119", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-120", + "isStable": false, + "releasedAt": "2021-05-12T20:39:42Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-121", + "isStable": false, + "releasedAt": "2021-05-12T20:56:19Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-121", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-122", + "isStable": false, + "releasedAt": "2021-05-14T20:36:24Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0-new-routing-eap-123", + "isStable": false, + "releasedAt": "2021-05-15T21:13:00Z", + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-new-routing-eap-123" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-128", + "isStable": false, + "releasedAt": "2021-05-20T20:45:06Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-130", + "isStable": false, + "releasedAt": "2021-05-21T20:12:26Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-130", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-131", + "isStable": false, + "releasedAt": "2021-05-24T06:38:41Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-132", + "isStable": false, + "releasedAt": "2021-05-24T11:21:02Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-132", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-133", + "isStable": false, + "releasedAt": "2021-05-24T11:34:46Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-134", + "isStable": false, + "releasedAt": "2021-05-25T20:45:20Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-135", + "isStable": false, + "releasedAt": "2021-05-25T21:22:47Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-135", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-136", + "isStable": false, + "releasedAt": "2021-05-26T20:46:13Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-137", + "isStable": false, + "releasedAt": "2021-05-27T20:44:50Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-138", + "isStable": false, + "releasedAt": "2021-05-27T20:58:47Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-138", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-139", + "isStable": false, + "releasedAt": "2021-05-28T20:12:51Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-140", + "isStable": false, + "releasedAt": "2021-05-29T20:49:05Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-141", + "isStable": false, + "releasedAt": "2021-05-31T20:12:00Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-142", + "isStable": false, + "releasedAt": "2021-05-31T20:25:08Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-142", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-143", + "isStable": false, + "releasedAt": "2021-06-01T21:41:16Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-144", + "isStable": false, + "releasedAt": "2021-06-02T20:44:43Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-144", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-145", + "isStable": false, + "releasedAt": "2021-06-04T20:13:16Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-146", + "isStable": false, + "releasedAt": "2021-06-07T20:13:16Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-147", + "isStable": false, + "releasedAt": "2021-06-07T20:27:22Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-147", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-148", + "isStable": false, + "releasedAt": "2021-06-08T20:45:27Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-148", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-149", + "isStable": false, + "releasedAt": "2021-06-09T21:09:04Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-150", + "isStable": false, + "releasedAt": "2021-06-09T21:21:38Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-150", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-151", + "isStable": false, + "releasedAt": "2021-06-10T20:13:08Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-152", + "isStable": false, + "releasedAt": "2021-06-10T20:26:31Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-152", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-153", + "isStable": false, + "releasedAt": "2021-06-15T20:35:36Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-155", + "isStable": false, + "releasedAt": "2021-06-16T20:48:44Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-156", + "isStable": false, + "releasedAt": "2021-06-16T21:07:16Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-156", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-158", + "isStable": false, + "releasedAt": "2021-06-17T20:48:16Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-158", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-157", + "isStable": false, + "releasedAt": "2021-06-17T21:06:07Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-159", + "isStable": false, + "releasedAt": "2021-06-26T20:46:43Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-160", + "isStable": false, + "releasedAt": "2021-06-28T20:16:16Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-161", + "isStable": false, + "releasedAt": "2021-06-28T20:34:42Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-161", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-162", + "isStable": false, + "releasedAt": "2021-06-29T20:49:22Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-162", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-163", + "isStable": false, + "releasedAt": "2021-06-30T20:47:14Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-164", + "isStable": false, + "releasedAt": "2021-07-01T20:48:00Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-167", + "isStable": false, + "releasedAt": "2021-07-02T20:16:58Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-167", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-166", + "isStable": false, + "releasedAt": "2021-07-02T20:42:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-168", + "isStable": false, + "releasedAt": "2021-07-05T20:47:36Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-168", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-169", + "isStable": false, + "releasedAt": "2021-07-06T20:15:40Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-169", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-170", + "isStable": false, + "releasedAt": "2021-07-09T20:43:44Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-171", + "isStable": false, + "releasedAt": "2021-07-12T20:15:43Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-171", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-172", + "isStable": false, + "releasedAt": "2021-07-14T20:15:16Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-173", + "isStable": false, + "releasedAt": "2021-07-16T20:42:45Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-173", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-174", + "isStable": false, + "releasedAt": "2021-07-19T20:44:23Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-175", + "isStable": false, + "releasedAt": "2021-07-20T20:41:28Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-177", + "isStable": false, + "releasedAt": "2021-07-21T20:41:50Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-177", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-178", + "isStable": false, + "releasedAt": "2021-07-23T20:12:13Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-178", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-180", + "isStable": false, + "releasedAt": "2021-07-26T20:12:10Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-180", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-179", + "isStable": false, + "releasedAt": "2021-07-26T20:39:31Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-181", + "isStable": false, + "releasedAt": "2021-07-28T20:40:56Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-182", + "isStable": false, + "releasedAt": "2021-07-29T20:40:50Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-184", + "isStable": false, + "releasedAt": "2021-08-03T20:13:48Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-184", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-183", + "isStable": false, + "releasedAt": "2021-08-03T20:41:21Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2-native-mm-eap-185", + "isStable": false, + "releasedAt": "2021-08-06T20:13:27Z", + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-native-mm-eap-185" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-186", + "isStable": false, + "releasedAt": "2021-08-06T20:35:53Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-186", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-187", + "isStable": false, + "releasedAt": "2021-08-09T20:16:20Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-187", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2-native-mm-eap-188", + "isStable": false, + "releasedAt": "2021-08-11T16:52:18Z", + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-native-mm-eap-188" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2-native-mm-eap-189", + "isStable": false, + "releasedAt": "2021-08-11T19:04:24Z", + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-native-mm-eap-189" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-191", + "isStable": false, + "releasedAt": "2021-08-11T20:14:37Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-191", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-190", + "isStable": false, + "releasedAt": "2021-08-11T20:40:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2-native-mm-eap-192", + "isStable": false, + "releasedAt": "2021-08-12T20:39:18Z", + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-native-mm-eap-192" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-194", + "isStable": false, + "releasedAt": "2021-08-16T20:14:22Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-194", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-193", + "isStable": false, + "releasedAt": "2021-08-16T20:40:04Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2-native-mm-eap-195", + "isStable": false, + "releasedAt": "2021-08-17T13:53:59Z", + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-native-mm-eap-195" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2-native-mm-eap-196", + "isStable": false, + "releasedAt": "2021-08-17T20:40:36Z", + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-native-mm-eap-196" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-197", + "isStable": false, + "releasedAt": "2021-08-18T20:43:25Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-198", + "isStable": false, + "releasedAt": "2021-08-20T20:44:46Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-200", + "isStable": false, + "releasedAt": "2021-08-23T20:16:53Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-200", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-199", + "isStable": false, + "releasedAt": "2021-08-23T20:41:06Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-201", + "isStable": false, + "releasedAt": "2021-08-25T20:43:29Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-202", + "isStable": false, + "releasedAt": "2021-08-26T20:41:53Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-203", + "isStable": false, + "releasedAt": "2021-08-27T20:42:01Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-205", + "isStable": false, + "releasedAt": "2021-08-31T20:14:03Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-205", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-204", + "isStable": false, + "releasedAt": "2021-08-31T20:42:34Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-211", + "isStable": false, + "releasedAt": "2021-09-08T20:17:11Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-212", + "isStable": false, + "releasedAt": "2021-09-08T20:45:14Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-212", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-213", + "isStable": false, + "releasedAt": "2021-09-09T20:44:01Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-213", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-214", + "isStable": false, + "releasedAt": "2021-09-13T20:15:33Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-215", + "isStable": false, + "releasedAt": "2021-09-13T20:44:47Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-215", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-216", + "isStable": false, + "releasedAt": "2021-09-15T20:13:14Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-217", + "isStable": false, + "releasedAt": "2021-09-15T20:45:25Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-217", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-218", + "isStable": false, + "releasedAt": "2021-09-16T20:41:21Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-219", + "isStable": false, + "releasedAt": "2021-09-16T21:01:50Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-219", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-220", + "isStable": false, + "releasedAt": "2021-09-20T20:40:43Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-221", + "isStable": false, + "releasedAt": "2021-09-22T17:51:28Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-222", + "isStable": false, + "releasedAt": "2021-09-22T18:49:02Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-ide-debug-eap-223", + "isStable": false, + "releasedAt": "2021-09-22T19:28:02Z", + "semanticPart": "2.0.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-ide-debug-eap-223" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-224", + "isStable": false, + "releasedAt": "2021-09-22T20:15:46Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-ide-debug-eap-225", + "isStable": false, + "releasedAt": "2021-09-22T20:47:23Z", + "semanticPart": "2.0.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-ide-debug-eap-225" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-226", + "isStable": false, + "releasedAt": "2021-09-23T20:15:37Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-227", + "isStable": false, + "releasedAt": "2021-09-27T20:42:45Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-228", + "isStable": false, + "releasedAt": "2021-09-28T20:36:40Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-228", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-229", + "isStable": false, + "releasedAt": "2021-09-29T20:45:06Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-229", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-230", + "isStable": false, + "releasedAt": "2021-09-30T20:15:43Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-231", + "isStable": false, + "releasedAt": "2021-10-01T20:40:54Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-232", + "isStable": false, + "releasedAt": "2021-10-04T20:45:32Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-233", + "isStable": false, + "releasedAt": "2021-10-04T21:07:11Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-233", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-234", + "isStable": false, + "releasedAt": "2021-10-05T20:43:57Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-235", + "isStable": false, + "releasedAt": "2021-10-06T20:43:58Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-236", + "isStable": false, + "releasedAt": "2021-10-11T20:14:16Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-237", + "isStable": false, + "releasedAt": "2021-10-12T20:43:13Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-238", + "isStable": false, + "releasedAt": "2021-10-13T20:13:41Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-240", + "isStable": false, + "releasedAt": "2021-10-14T12:31:02Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-241", + "isStable": false, + "releasedAt": "2021-10-14T20:14:23Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-242", + "isStable": false, + "releasedAt": "2021-10-15T20:47:08Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-243", + "isStable": false, + "releasedAt": "2021-10-18T20:43:23Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-244", + "isStable": false, + "releasedAt": "2021-10-19T16:01:31Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-245", + "isStable": false, + "releasedAt": "2021-10-19T17:19:44Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-246", + "isStable": false, + "releasedAt": "2021-10-19T20:13:01Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-247", + "isStable": false, + "releasedAt": "2021-10-20T15:24:33Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-248", + "isStable": false, + "releasedAt": "2021-10-20T20:12:54Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-249", + "isStable": false, + "releasedAt": "2021-10-21T20:46:43Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-250", + "isStable": false, + "releasedAt": "2021-10-22T20:19:13Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-251", + "isStable": false, + "releasedAt": "2021-10-23T20:13:03Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "main-252", + "isStable": false, + "releasedAt": "2021-10-24T20:13:18Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-253", + "isStable": false, + "releasedAt": "2021-10-25T14:32:06Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-253", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-254", + "isStable": false, + "releasedAt": "2021-10-25T15:31:19Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-254", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-255", + "isStable": false, + "releasedAt": "2021-10-25T19:40:58Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-255", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-256", + "isStable": false, + "releasedAt": "2021-10-26T12:51:59Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-256", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-257", + "isStable": false, + "releasedAt": "2021-10-26T14:33:25Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-257", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-258", + "isStable": false, + "releasedAt": "2021-10-27T20:43:14Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-258", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-259", + "isStable": false, + "releasedAt": "2021-10-29T20:15:49Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-259", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-260", + "isStable": false, + "releasedAt": "2021-11-02T20:15:16Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-260", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-261", + "isStable": false, + "releasedAt": "2021-11-03T20:12:58Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-261", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-262", + "isStable": false, + "releasedAt": "2021-11-05T20:15:06Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-262", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-263", + "isStable": false, + "releasedAt": "2021-11-07T20:15:15Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-263", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-265", + "isStable": false, + "releasedAt": "2021-11-11T20:15:27Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-265", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-266", + "isStable": false, + "releasedAt": "2021-11-12T20:15:24Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-266", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-267", + "isStable": false, + "releasedAt": "2021-11-16T20:47:41Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-267", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-268", + "isStable": false, + "releasedAt": "2021-11-17T20:18:37Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-268", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-269", + "isStable": false, + "releasedAt": "2021-11-18T20:48:11Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-269", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-270", + "isStable": false, + "releasedAt": "2021-11-19T20:15:29Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-270", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-271", + "isStable": false, + "releasedAt": "2021-11-22T20:19:57Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-271", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-272", + "isStable": false, + "releasedAt": "2021-11-23T20:47:55Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-272", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-273", + "isStable": false, + "releasedAt": "2021-11-24T20:47:31Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-273", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-274", + "isStable": false, + "releasedAt": "2021-11-25T20:47:37Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-274", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-275", + "isStable": false, + "releasedAt": "2021-11-26T20:17:45Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-275", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-276", + "isStable": false, + "releasedAt": "2021-11-29T20:47:07Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-276", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-277", + "isStable": false, + "releasedAt": "2021-11-30T20:47:32Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-277", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-278", + "isStable": false, + "releasedAt": "2021-12-03T20:45:31Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-278", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "-eap-279", + "isStable": false, + "releasedAt": "2021-12-06T12:19:17Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "-eap-281", + "isStable": false, + "releasedAt": "2021-12-06T14:32:15Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.7-eap-282", + "isStable": false, + "releasedAt": "2021-12-06T15:18:43Z", + "semanticPart": "1.6.7", + "stabilityMarker": "-eap-282", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-283", + "isStable": false, + "releasedAt": "2021-12-08T20:48:03Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-283", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-284", + "isStable": false, + "releasedAt": "2021-12-16T03:02:21Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-284", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-285", + "isStable": false, + "releasedAt": "2021-12-16T20:16:14Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-285", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-286", + "isStable": false, + "releasedAt": "2021-12-17T20:16:29Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-286", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-287", + "isStable": false, + "releasedAt": "2021-12-20T20:16:16Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-287", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-288", + "isStable": false, + "releasedAt": "2021-12-22T20:16:06Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-288", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-289", + "isStable": false, + "releasedAt": "2021-12-23T20:16:04Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-289", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-290", + "isStable": false, + "releasedAt": "2022-01-07T20:15:43Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-290", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-291", + "isStable": false, + "releasedAt": "2022-01-11T20:16:19Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-291", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-292", + "isStable": false, + "releasedAt": "2022-01-11T20:36:34Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-292", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-293", + "isStable": false, + "releasedAt": "2022-01-13T20:17:24Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-293", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-294", + "isStable": false, + "releasedAt": "2022-01-13T20:32:19Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-294", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-295", + "isStable": false, + "releasedAt": "2022-01-14T20:21:56Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-295", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-296", + "isStable": false, + "releasedAt": "2022-01-14T20:51:19Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-296", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-297", + "isStable": false, + "releasedAt": "2022-01-17T20:17:01Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-297", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-298", + "isStable": false, + "releasedAt": "2022-01-17T20:43:43Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-298", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-299", + "isStable": false, + "releasedAt": "2022-01-18T20:17:18Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-299", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-300", + "isStable": false, + "releasedAt": "2022-01-19T20:16:54Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-300", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0-eap-301", + "isStable": false, + "releasedAt": "2022-01-19T20:39:49Z", + "semanticPart": "2.0", + "stabilityMarker": "-eap-301", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-302", + "isStable": false, + "releasedAt": "2022-01-21T20:17:21Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-302", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-310", + "isStable": false, + "releasedAt": "2022-01-27T13:17:54Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-310", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-312", + "isStable": false, + "releasedAt": "2022-01-27T16:55:05Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-312", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-313", + "isStable": false, + "releasedAt": "2022-01-27T20:10:31Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-313", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-314", + "isStable": false, + "releasedAt": "2022-01-27T20:26:28Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-314", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-315", + "isStable": false, + "releasedAt": "2022-01-28T12:35:54Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-315", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-316", + "isStable": false, + "releasedAt": "2022-01-28T20:12:32Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-316", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-317", + "isStable": false, + "releasedAt": "2022-01-28T20:27:16Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-317", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-318", + "isStable": false, + "releasedAt": "2022-01-31T20:10:21Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-318", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-319", + "isStable": false, + "releasedAt": "2022-02-01T20:12:26Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-319", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-320", + "isStable": false, + "releasedAt": "2022-02-01T20:39:43Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-320", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-321", + "isStable": false, + "releasedAt": "2022-02-02T20:12:08Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-321", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-322", + "isStable": false, + "releasedAt": "2022-02-02T20:40:02Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-322", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-323", + "isStable": false, + "releasedAt": "2022-02-03T20:10:36Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-323", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-324", + "isStable": false, + "releasedAt": "2022-02-03T20:26:06Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-324", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-325", + "isStable": false, + "releasedAt": "2022-02-04T20:10:22Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-325", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-326", + "isStable": false, + "releasedAt": "2022-02-07T20:38:48Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-326", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-327", + "isStable": false, + "releasedAt": "2022-02-08T20:12:12Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-327", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-328", + "isStable": false, + "releasedAt": "2022-02-09T20:12:05Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-328", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-329", + "isStable": false, + "releasedAt": "2022-02-10T20:10:19Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-329", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-330", + "isStable": false, + "releasedAt": "2022-02-14T20:10:16Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-330", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-331", + "isStable": false, + "releasedAt": "2022-02-16T20:10:22Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-331", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-332", + "isStable": false, + "releasedAt": "2022-02-17T20:40:19Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-332", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-333", + "isStable": false, + "releasedAt": "2022-02-21T20:10:42Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-333", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-334", + "isStable": false, + "releasedAt": "2022-02-22T11:24:42Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-334", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-336", + "isStable": false, + "releasedAt": "2022-03-02T20:39:29Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-336", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-337", + "isStable": false, + "releasedAt": "2022-03-03T20:39:23Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-337", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8-eap-339", + "isStable": false, + "releasedAt": "2022-03-04T20:39:22Z", + "semanticPart": "1.6.8", + "stabilityMarker": "-eap-339", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-338", + "isStable": false, + "releasedAt": "2022-03-04T20:52:17Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-338", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8-eap-341", + "isStable": false, + "releasedAt": "2022-03-07T20:39:45Z", + "semanticPart": "1.6.8", + "stabilityMarker": "-eap-341", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-340", + "isStable": false, + "releasedAt": "2022-03-07T20:52:40Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-340", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8-eap-343", + "isStable": false, + "releasedAt": "2022-03-08T20:45:26Z", + "semanticPart": "1.6.8", + "stabilityMarker": "-eap-343", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-342", + "isStable": false, + "releasedAt": "2022-03-08T20:59:52Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-342", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8-eap-344", + "isStable": false, + "releasedAt": "2022-03-09T14:02:26Z", + "semanticPart": "1.6.8", + "stabilityMarker": "-eap-344", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8-eap-346", + "isStable": false, + "releasedAt": "2022-03-09T20:47:21Z", + "semanticPart": "1.6.8", + "stabilityMarker": "-eap-346", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-347", + "isStable": false, + "releasedAt": "2022-03-10T20:39:06Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-347", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-348", + "isStable": false, + "releasedAt": "2022-03-11T20:11:24Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-348", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8-eap-349", + "isStable": false, + "releasedAt": "2022-03-11T20:44:17Z", + "semanticPart": "1.6.8", + "stabilityMarker": "-eap-349", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-350", + "isStable": false, + "releasedAt": "2022-03-14T20:12:09Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-350", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-351", + "isStable": false, + "releasedAt": "2022-03-15T20:12:17Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-351", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-352", + "isStable": false, + "releasedAt": "2022-03-16T20:14:02Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-352", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-353", + "isStable": false, + "releasedAt": "2022-03-17T20:10:10Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-353", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-354", + "isStable": false, + "releasedAt": "2022-03-18T20:13:50Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-354", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-355", + "isStable": false, + "releasedAt": "2022-03-21T20:10:07Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-355", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-356", + "isStable": false, + "releasedAt": "2022-03-22T20:11:27Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-356", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-357", + "isStable": false, + "releasedAt": "2022-03-23T20:09:45Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-357", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-358", + "isStable": false, + "releasedAt": "2022-03-24T20:10:04Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-358", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-359", + "isStable": false, + "releasedAt": "2022-03-25T20:12:07Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-359", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-360", + "isStable": false, + "releasedAt": "2022-03-28T20:41:44Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-360", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-361", + "isStable": false, + "releasedAt": "2022-03-30T20:11:41Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-361", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-362", + "isStable": false, + "releasedAt": "2022-03-31T20:38:07Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-362", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-eap-363", + "isStable": false, + "releasedAt": "2022-04-03T20:13:23Z", + "semanticPart": "2.0.0", + "stabilityMarker": "-eap-363", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2-eap-364", + "isStable": false, + "releasedAt": "2022-04-04T20:09:35Z", + "semanticPart": "2", + "stabilityMarker": "-eap-364", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2-eap-365", + "isStable": false, + "releasedAt": "2022-04-05T20:11:40Z", + "semanticPart": "2", + "stabilityMarker": "-eap-365", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2-eap-366", + "isStable": false, + "releasedAt": "2022-04-06T20:40:17Z", + "semanticPart": "2", + "stabilityMarker": "-eap-366", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "-eap-367", + "isStable": false, + "releasedAt": "2022-04-08T20:38:04Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "-eap-368", + "isStable": false, + "releasedAt": "2022-04-11T20:09:58Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "-eap-369", + "isStable": false, + "releasedAt": "2022-04-14T12:57:58Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "garbage", + "versionName": "-eap-370", + "isStable": false, + "releasedAt": "2022-04-14T20:39:23Z" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-371", + "isStable": false, + "releasedAt": "2022-04-19T21:45:42Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-371", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-372", + "isStable": false, + "releasedAt": "2022-04-20T09:46:41Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-372", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-373", + "isStable": false, + "releasedAt": "2022-04-20T21:41:35Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-373", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-374", + "isStable": false, + "releasedAt": "2022-04-21T09:01:16Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-374", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-375", + "isStable": false, + "releasedAt": "2022-04-21T13:25:14Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-375", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-376", + "isStable": false, + "releasedAt": "2022-04-21T20:44:48Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-376", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-377", + "isStable": false, + "releasedAt": "2022-04-25T20:10:00Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-377", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-378", + "isStable": false, + "releasedAt": "2022-04-26T20:11:26Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-378", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-379", + "isStable": false, + "releasedAt": "2022-04-27T20:38:41Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-379", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-380", + "isStable": false, + "releasedAt": "2022-04-27T20:52:47Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-380", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-381", + "isStable": false, + "releasedAt": "2022-04-28T11:38:45Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-381", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-382", + "isStable": false, + "releasedAt": "2022-04-28T16:04:29Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-382", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-383", + "isStable": false, + "releasedAt": "2022-04-28T20:11:26Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-383", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-384", + "isStable": false, + "releasedAt": "2022-04-28T20:38:57Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-384", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1-eap-385", + "isStable": false, + "releasedAt": "2022-04-29T20:09:54Z", + "semanticPart": "2.0.1", + "stabilityMarker": "-eap-385", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-386", + "isStable": false, + "releasedAt": "2022-05-02T14:28:20Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-386", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-387", + "isStable": false, + "releasedAt": "2022-05-02T20:41:55Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-387", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-388", + "isStable": false, + "releasedAt": "2022-05-03T20:11:15Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-388", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-389", + "isStable": false, + "releasedAt": "2022-05-05T09:20:58Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-389", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-390", + "isStable": false, + "releasedAt": "2022-05-05T20:11:43Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-390", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-391", + "isStable": false, + "releasedAt": "2022-05-06T06:54:44Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-391", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-392", + "isStable": false, + "releasedAt": "2022-05-06T20:38:44Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-392", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-393", + "isStable": false, + "releasedAt": "2022-05-09T07:44:27Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-393", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-394", + "isStable": false, + "releasedAt": "2022-05-09T20:11:15Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-394", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-395", + "isStable": false, + "releasedAt": "2022-05-10T20:10:38Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-395", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-396", + "isStable": false, + "releasedAt": "2022-05-11T20:09:57Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-396", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-397", + "isStable": false, + "releasedAt": "2022-05-16T20:12:44Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-397", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-398", + "isStable": false, + "releasedAt": "2022-05-17T20:10:22Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-398", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-399", + "isStable": false, + "releasedAt": "2022-05-18T20:11:14Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-399", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-400", + "isStable": false, + "releasedAt": "2022-05-18T20:29:24Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-400", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-401", + "isStable": false, + "releasedAt": "2022-05-19T20:11:29Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-401", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-402", + "isStable": false, + "releasedAt": "2022-05-19T20:25:53Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-402", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-403", + "isStable": false, + "releasedAt": "2022-05-20T20:13:51Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-403", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-404", + "isStable": false, + "releasedAt": "2022-05-20T20:34:20Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-404", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-405", + "isStable": false, + "releasedAt": "2022-05-23T20:13:53Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-405", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-406", + "isStable": false, + "releasedAt": "2022-05-25T20:11:05Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-406", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-407", + "isStable": false, + "releasedAt": "2022-05-27T20:11:12Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-407", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-408", + "isStable": false, + "releasedAt": "2022-05-30T20:11:17Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-408", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-409", + "isStable": false, + "releasedAt": "2022-05-31T20:10:40Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-409", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-eap-410", + "isStable": false, + "releasedAt": "2022-05-31T20:31:57Z", + "semanticPart": "2.0.2", + "stabilityMarker": "-eap-410", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-411", + "isStable": false, + "releasedAt": "2022-06-01T20:12:18Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-411", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-412", + "isStable": false, + "releasedAt": "2022-06-01T20:26:18Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-412", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-413", + "isStable": false, + "releasedAt": "2022-06-02T20:11:35Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-413", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-414", + "isStable": false, + "releasedAt": "2022-06-03T20:12:55Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-414", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-415", + "isStable": false, + "releasedAt": "2022-06-07T20:10:15Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-415", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-416", + "isStable": false, + "releasedAt": "2022-06-08T20:11:12Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-416", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-417", + "isStable": false, + "releasedAt": "2022-06-08T20:26:20Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-417", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-418", + "isStable": false, + "releasedAt": "2022-06-13T20:11:56Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-418", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-419", + "isStable": false, + "releasedAt": "2022-06-13T20:27:13Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-419", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-420", + "isStable": false, + "releasedAt": "2022-06-14T20:11:30Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-420", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-421", + "isStable": false, + "releasedAt": "2022-06-15T20:13:18Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-421", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-422", + "isStable": false, + "releasedAt": "2022-06-17T20:12:46Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-422", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-423", + "isStable": false, + "releasedAt": "2022-06-20T08:45:12Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-423", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-424", + "isStable": false, + "releasedAt": "2022-06-20T20:10:38Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-424", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-425", + "isStable": false, + "releasedAt": "2022-06-21T20:12:50Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-425", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-426", + "isStable": false, + "releasedAt": "2022-06-22T20:10:45Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-426", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-427", + "isStable": false, + "releasedAt": "2022-06-23T20:12:10Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-427", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-428", + "isStable": false, + "releasedAt": "2022-06-23T20:26:33Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-428", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-429", + "isStable": false, + "releasedAt": "2022-06-27T20:10:25Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-429", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3-eap-430", + "isStable": false, + "releasedAt": "2022-06-28T20:09:45Z", + "semanticPart": "2.0.3", + "stabilityMarker": "-eap-430", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-431", + "isStable": false, + "releasedAt": "2022-06-28T20:24:13Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-431", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-432", + "isStable": false, + "releasedAt": "2022-06-30T20:10:26Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-432", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-433", + "isStable": false, + "releasedAt": "2022-07-04T20:10:38Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-433", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-434", + "isStable": false, + "releasedAt": "2022-07-06T20:11:03Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-434", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-435", + "isStable": false, + "releasedAt": "2022-07-07T20:11:01Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-435", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-436", + "isStable": false, + "releasedAt": "2022-07-08T20:10:32Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-436", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-437", + "isStable": false, + "releasedAt": "2022-07-08T20:25:05Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-437", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-1.7.0-eap-438", + "isStable": false, + "releasedAt": "2022-07-12T13:33:54Z", + "semanticPart": "2.0.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-1.7.0-eap-438" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-1.7.0-eap-439", + "isStable": false, + "releasedAt": "2022-07-12T20:10:09Z", + "semanticPart": "2.0.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-1.7.0-eap-439" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2-1.7.0-eap-441", + "isStable": false, + "releasedAt": "2022-07-13T08:05:51Z", + "semanticPart": "2.0.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-1.7.0-eap-441" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-442", + "isStable": false, + "releasedAt": "2022-07-13T20:10:50Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-442", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-443", + "isStable": false, + "releasedAt": "2022-07-14T20:13:39Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-443", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-444", + "isStable": false, + "releasedAt": "2022-07-21T08:46:58Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-444", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-445", + "isStable": false, + "releasedAt": "2022-07-21T20:11:23Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-445", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-446", + "isStable": false, + "releasedAt": "2022-07-22T20:14:17Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-446", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-447", + "isStable": false, + "releasedAt": "2022-07-22T21:08:47Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-447", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-448", + "isStable": false, + "releasedAt": "2022-07-25T07:54:45Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-448" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-449", + "isStable": false, + "releasedAt": "2022-07-25T20:11:11Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-449", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-450", + "isStable": false, + "releasedAt": "2022-07-26T06:08:44Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-450" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-451", + "isStable": false, + "releasedAt": "2022-07-26T10:05:59Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-451" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-452", + "isStable": false, + "releasedAt": "2022-07-26T14:09:09Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-452" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-453", + "isStable": false, + "releasedAt": "2022-07-26T20:07:25Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-453", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-454", + "isStable": false, + "releasedAt": "2022-07-27T08:33:52Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-454" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-455", + "isStable": false, + "releasedAt": "2022-07-28T09:44:14Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-455" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-456", + "isStable": false, + "releasedAt": "2022-07-28T12:47:54Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-456" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-457", + "isStable": false, + "releasedAt": "2022-07-29T09:44:32Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-457" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-458", + "isStable": false, + "releasedAt": "2022-08-08T20:07:33Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-458", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-459", + "isStable": false, + "releasedAt": "2022-08-08T20:17:20Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-459" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-460", + "isStable": false, + "releasedAt": "2022-08-09T08:50:41Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-460" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-461", + "isStable": false, + "releasedAt": "2022-08-09T09:25:24Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-461" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-462", + "isStable": false, + "releasedAt": "2022-08-09T09:34:57Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-462" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-463", + "isStable": false, + "releasedAt": "2022-08-09T20:07:56Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-463" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-465", + "isStable": false, + "releasedAt": "2022-08-10T07:18:01Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-465" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-space-eap-466", + "isStable": false, + "releasedAt": "2022-08-10T07:26:06Z", + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": "-space-eap-466" + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-467", + "isStable": false, + "releasedAt": "2022-08-10T20:08:51Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-467", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-468", + "isStable": false, + "releasedAt": "2022-08-11T20:09:43Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-468", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-470", + "isStable": false, + "releasedAt": "2022-08-15T20:09:36Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-470", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-472", + "isStable": false, + "releasedAt": "2022-08-17T20:12:33Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-472", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-473", + "isStable": false, + "releasedAt": "2022-08-18T20:08:24Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-473", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-474", + "isStable": false, + "releasedAt": "2022-08-18T20:16:30Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-474", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-475", + "isStable": false, + "releasedAt": "2022-08-20T20:12:23Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-475", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-476", + "isStable": false, + "releasedAt": "2022-08-22T20:09:05Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-476", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0-eap-479", + "isStable": false, + "releasedAt": "2022-08-24T20:13:44Z", + "semanticPart": "2.1.0", + "stabilityMarker": "-eap-479", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-480", + "isStable": false, + "releasedAt": "2022-08-25T20:09:27Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-480", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-481", + "isStable": false, + "releasedAt": "2022-08-25T20:17:56Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-481", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-482", + "isStable": false, + "releasedAt": "2022-08-26T20:09:07Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-482", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-483", + "isStable": false, + "releasedAt": "2022-08-26T20:17:01Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-483", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-484", + "isStable": false, + "releasedAt": "2022-08-29T20:09:41Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-484", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-485", + "isStable": false, + "releasedAt": "2022-08-30T20:07:59Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-485", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-486", + "isStable": false, + "releasedAt": "2022-08-31T20:07:34Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-486", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-487", + "isStable": false, + "releasedAt": "2022-08-31T20:15:38Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-487", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-488", + "isStable": false, + "releasedAt": "2022-09-01T20:09:41Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-488", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-489", + "isStable": false, + "releasedAt": "2022-09-02T20:07:22Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-489", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-490", + "isStable": false, + "releasedAt": "2022-09-02T20:15:13Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-490", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-491", + "isStable": false, + "releasedAt": "2022-09-05T08:08:59Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-491", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-492", + "isStable": false, + "releasedAt": "2022-09-05T08:54:06Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-492", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-493", + "isStable": false, + "releasedAt": "2022-09-05T20:07:28Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-493", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-494", + "isStable": false, + "releasedAt": "2022-09-05T20:16:46Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-494", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-495", + "isStable": false, + "releasedAt": "2022-09-06T20:08:15Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-495", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-496", + "isStable": false, + "releasedAt": "2022-09-06T20:15:53Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-496", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-497", + "isStable": false, + "releasedAt": "2022-09-07T20:08:56Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-497", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-498", + "isStable": false, + "releasedAt": "2022-09-07T20:16:35Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-498", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-499", + "isStable": false, + "releasedAt": "2022-09-08T20:07:09Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-499", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-500", + "isStable": false, + "releasedAt": "2022-09-08T20:14:40Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-500", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-501", + "isStable": false, + "releasedAt": "2022-09-26T20:10:19Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-501", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-502", + "isStable": false, + "releasedAt": "2022-09-26T20:21:36Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-502", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-503", + "isStable": false, + "releasedAt": "2022-09-27T20:08:24Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-503", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-504", + "isStable": false, + "releasedAt": "2022-09-27T20:19:09Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-504", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-505", + "isStable": false, + "releasedAt": "2022-09-28T20:15:11Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-505", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-506", + "isStable": false, + "releasedAt": "2022-09-29T20:10:46Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-506", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-507", + "isStable": false, + "releasedAt": "2022-09-29T20:25:32Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-507", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-508", + "isStable": false, + "releasedAt": "2022-09-30T20:09:28Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-508", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-509", + "isStable": false, + "releasedAt": "2022-10-03T20:08:50Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-509", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-510", + "isStable": false, + "releasedAt": "2022-10-04T20:11:26Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-510", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-511", + "isStable": false, + "releasedAt": "2022-10-05T20:08:34Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-511", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2-eap-512", + "isStable": false, + "releasedAt": "2022-10-05T20:20:38Z", + "semanticPart": "2.1.2", + "stabilityMarker": "-eap-512", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-513", + "isStable": false, + "releasedAt": "2022-10-06T20:10:22Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-513", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1-eap-514", + "isStable": false, + "releasedAt": "2022-10-06T20:21:05Z", + "semanticPart": "2.1.1", + "stabilityMarker": "-eap-514", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-516", + "isStable": false, + "releasedAt": "2022-10-10T20:11:21Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-516", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-515", + "isStable": false, + "releasedAt": "2022-10-10T20:28:35Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-515", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-517", + "isStable": false, + "releasedAt": "2022-10-11T20:11:40Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-517", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-518", + "isStable": false, + "releasedAt": "2022-10-12T20:08:38Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-518", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-519", + "isStable": false, + "releasedAt": "2022-10-12T20:16:03Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-519", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-520", + "isStable": false, + "releasedAt": "2022-10-14T20:09:17Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-520", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-521", + "isStable": false, + "releasedAt": "2022-10-17T20:09:18Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-521", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-522", + "isStable": false, + "releasedAt": "2022-10-18T20:09:18Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-522", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-523", + "isStable": false, + "releasedAt": "2022-10-19T07:59:04Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-523", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-524", + "isStable": false, + "releasedAt": "2022-10-19T20:06:56Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-524", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-525", + "isStable": false, + "releasedAt": "2022-10-20T20:08:55Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-525", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-526", + "isStable": false, + "releasedAt": "2022-10-24T20:06:46Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-526", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-527", + "isStable": false, + "releasedAt": "2022-10-24T20:14:11Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-527", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-528", + "isStable": false, + "releasedAt": "2022-10-25T20:09:18Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-528", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-529", + "isStable": false, + "releasedAt": "2022-10-25T20:18:28Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-529", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-531", + "isStable": false, + "releasedAt": "2022-10-26T10:12:23Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-531", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-532", + "isStable": false, + "releasedAt": "2022-10-26T20:07:04Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-532", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-533", + "isStable": false, + "releasedAt": "2022-10-27T20:06:59Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-533", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-534", + "isStable": false, + "releasedAt": "2022-10-28T20:06:28Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-534", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-535", + "isStable": false, + "releasedAt": "2022-10-31T20:07:30Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-535", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-536", + "isStable": false, + "releasedAt": "2022-11-02T20:06:53Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-536", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-537", + "isStable": false, + "releasedAt": "2022-11-02T20:14:22Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-537", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3-eap-538", + "isStable": false, + "releasedAt": "2022-11-03T20:06:51Z", + "semanticPart": "2.1.3", + "stabilityMarker": "-eap-538", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-539", + "isStable": false, + "releasedAt": "2022-11-04T20:07:25Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-539", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-540", + "isStable": false, + "releasedAt": "2022-11-07T20:07:19Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-540", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-541", + "isStable": false, + "releasedAt": "2022-11-08T20:07:20Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-541", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-542", + "isStable": false, + "releasedAt": "2022-11-09T20:08:32Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-542", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-543", + "isStable": false, + "releasedAt": "2022-11-10T20:06:49Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-543", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-544", + "isStable": false, + "releasedAt": "2022-11-11T20:07:48Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-544", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-545", + "isStable": false, + "releasedAt": "2022-11-14T20:10:02Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-545", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-546", + "isStable": false, + "releasedAt": "2022-11-15T20:06:56Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-546", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-547", + "isStable": false, + "releasedAt": "2022-11-16T20:08:17Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-547", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-548", + "isStable": false, + "releasedAt": "2022-11-17T20:07:45Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-548", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-549", + "isStable": false, + "releasedAt": "2022-11-18T20:06:41Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-549", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-550", + "isStable": false, + "releasedAt": "2022-11-21T08:11:06Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-550", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-551", + "isStable": false, + "releasedAt": "2022-11-21T20:06:58Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-551", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-552", + "isStable": false, + "releasedAt": "2022-11-22T20:10:13Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-552", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-553", + "isStable": false, + "releasedAt": "2022-11-23T17:18:58Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-553", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-554", + "isStable": false, + "releasedAt": "2022-11-23T20:10:09Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-554", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-555", + "isStable": false, + "releasedAt": "2022-11-24T20:08:37Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-555", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-557", + "isStable": false, + "releasedAt": "2022-11-25T20:11:05Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-557", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-558", + "isStable": false, + "releasedAt": "2022-11-28T20:07:13Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-558", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-559", + "isStable": false, + "releasedAt": "2022-11-28T20:14:52Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-559", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-560", + "isStable": false, + "releasedAt": "2022-11-29T20:08:27Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-560", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-561", + "isStable": false, + "releasedAt": "2022-11-30T20:09:15Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-561", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-562", + "isStable": false, + "releasedAt": "2022-12-01T20:07:38Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-562", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-563", + "isStable": false, + "releasedAt": "2022-12-02T20:10:59Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-563", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0-eap-564", + "isStable": false, + "releasedAt": "2022-12-06T20:07:17Z", + "semanticPart": "2.2.0", + "stabilityMarker": "-eap-564", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.1-eap-565", + "isStable": false, + "releasedAt": "2022-12-07T18:59:06Z", + "semanticPart": "2.2.1", + "stabilityMarker": "-eap-565", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.1-eap-566", + "isStable": false, + "releasedAt": "2022-12-07T20:05:49Z", + "semanticPart": "2.2.1", + "stabilityMarker": "-eap-566", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-567", + "isStable": false, + "releasedAt": "2022-12-08T20:04:07Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-567", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-568", + "isStable": false, + "releasedAt": "2022-12-09T20:04:03Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-568", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-569", + "isStable": false, + "releasedAt": "2022-12-12T20:06:29Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-569", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-570", + "isStable": false, + "releasedAt": "2022-12-12T20:12:48Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-570", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-571", + "isStable": false, + "releasedAt": "2022-12-13T20:04:52Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-571", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-572", + "isStable": false, + "releasedAt": "2022-12-13T20:13:36Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-572", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-573", + "isStable": false, + "releasedAt": "2022-12-14T20:04:19Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-573", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-574", + "isStable": false, + "releasedAt": "2022-12-14T20:12:50Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-574", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-575", + "isStable": false, + "releasedAt": "2022-12-15T20:05:41Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-575", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-576", + "isStable": false, + "releasedAt": "2022-12-19T20:03:53Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-576", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-577", + "isStable": false, + "releasedAt": "2022-12-19T20:12:18Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-577", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-578", + "isStable": false, + "releasedAt": "2022-12-20T20:07:44Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-578", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-579", + "isStable": false, + "releasedAt": "2022-12-20T20:15:58Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-579", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-580", + "isStable": false, + "releasedAt": "2022-12-27T20:06:39Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-580", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-581", + "isStable": false, + "releasedAt": "2022-12-28T20:04:11Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-581", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-582", + "isStable": false, + "releasedAt": "2023-01-02T20:08:30Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-582", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-583", + "isStable": false, + "releasedAt": "2023-01-03T20:05:56Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-583", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-584", + "isStable": false, + "releasedAt": "2023-01-05T20:04:31Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-584", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-585", + "isStable": false, + "releasedAt": "2023-01-05T20:10:54Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-585", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-586", + "isStable": false, + "releasedAt": "2023-01-06T20:04:01Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-586", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-587", + "isStable": false, + "releasedAt": "2023-01-09T20:06:16Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-587", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2-eap-588", + "isStable": false, + "releasedAt": "2023-01-09T20:15:00Z", + "semanticPart": "2.2.2", + "stabilityMarker": "-eap-588", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-589", + "isStable": false, + "releasedAt": "2023-01-10T20:06:25Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-589", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-590", + "isStable": false, + "releasedAt": "2023-01-11T20:07:39Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-590", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-591", + "isStable": false, + "releasedAt": "2023-01-12T20:07:23Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-591", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-592", + "isStable": false, + "releasedAt": "2023-01-16T20:09:31Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-592", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-593", + "isStable": false, + "releasedAt": "2023-01-16T20:18:28Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-593", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-594", + "isStable": false, + "releasedAt": "2023-01-17T20:04:43Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-594", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-595", + "isStable": false, + "releasedAt": "2023-01-18T20:07:54Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-595", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-596", + "isStable": false, + "releasedAt": "2023-01-19T20:08:03Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-596", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-597", + "isStable": false, + "releasedAt": "2023-01-23T20:07:33Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-597", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-598", + "isStable": false, + "releasedAt": "2023-01-24T20:09:41Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-598", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-599", + "isStable": false, + "releasedAt": "2023-01-30T20:07:29Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-599", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-600", + "isStable": false, + "releasedAt": "2023-01-31T20:08:30Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-600", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-601", + "isStable": false, + "releasedAt": "2023-02-06T20:06:28Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-601", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-602", + "isStable": false, + "releasedAt": "2023-02-06T20:11:24Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-602", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-603", + "isStable": false, + "releasedAt": "2023-02-09T20:07:41Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-603", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-604", + "isStable": false, + "releasedAt": "2023-02-10T20:03:58Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-604", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-605", + "isStable": false, + "releasedAt": "2023-02-13T20:05:51Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-605", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-606", + "isStable": false, + "releasedAt": "2023-02-13T20:17:53Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-606", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-607", + "isStable": false, + "releasedAt": "2023-02-14T20:04:26Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-607", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-608", + "isStable": false, + "releasedAt": "2023-02-17T20:09:04Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-608", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-609", + "isStable": false, + "releasedAt": "2023-02-17T20:13:10Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-609", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-610", + "isStable": false, + "releasedAt": "2023-02-21T20:04:30Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-610", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-611", + "isStable": false, + "releasedAt": "2023-02-22T20:04:18Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-611", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-612", + "isStable": false, + "releasedAt": "2023-02-22T20:14:33Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-612", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-613", + "isStable": false, + "releasedAt": "2023-02-24T20:04:11Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-613", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-614", + "isStable": false, + "releasedAt": "2023-02-24T20:11:37Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-614", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4-eap-615", + "isStable": false, + "releasedAt": "2023-02-28T20:04:33Z", + "semanticPart": "2.2.4", + "stabilityMarker": "-eap-615", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-616", + "isStable": false, + "releasedAt": "2023-02-28T20:10:53Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-616", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-617", + "isStable": false, + "releasedAt": "2023-03-01T20:03:49Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-617", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-618", + "isStable": false, + "releasedAt": "2023-03-03T20:06:09Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-618", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-619", + "isStable": false, + "releasedAt": "2023-03-06T20:04:05Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-619", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-620", + "isStable": false, + "releasedAt": "2023-03-08T20:06:56Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-620", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-621", + "isStable": false, + "releasedAt": "2023-03-09T20:04:00Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-621", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-622", + "isStable": false, + "releasedAt": "2023-03-10T20:05:07Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-622", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-623", + "isStable": false, + "releasedAt": "2023-03-14T20:07:58Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-623", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-624", + "isStable": false, + "releasedAt": "2023-03-15T20:05:29Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-624", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-625", + "isStable": false, + "releasedAt": "2023-03-16T20:04:04Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-625", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-626", + "isStable": false, + "releasedAt": "2023-03-17T20:06:49Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-626", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-627", + "isStable": false, + "releasedAt": "2023-03-20T20:03:56Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-627", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-628", + "isStable": false, + "releasedAt": "2023-03-21T20:06:16Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-628", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-629", + "isStable": false, + "releasedAt": "2023-03-22T20:05:15Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-629", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-630", + "isStable": false, + "releasedAt": "2023-03-23T20:05:06Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-630", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-631", + "isStable": false, + "releasedAt": "2023-03-24T20:05:20Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-631", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-632", + "isStable": false, + "releasedAt": "2023-03-25T20:06:23Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-632", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-633", + "isStable": false, + "releasedAt": "2023-03-27T20:06:19Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-633", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-634", + "isStable": false, + "releasedAt": "2023-03-28T20:06:41Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-634", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-635", + "isStable": false, + "releasedAt": "2023-03-29T20:07:40Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-635", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-636", + "isStable": false, + "releasedAt": "2023-03-30T20:06:31Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-636", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-637", + "isStable": false, + "releasedAt": "2023-04-03T20:08:00Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-637", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-638", + "isStable": false, + "releasedAt": "2023-04-03T20:13:11Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-638", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-640", + "isStable": false, + "releasedAt": "2023-04-05T20:05:04Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-640", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3-eap-645", + "isStable": false, + "releasedAt": "2023-04-08T12:45:08Z", + "semanticPart": "2.2.3", + "stabilityMarker": "-eap-645", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-648", + "isStable": false, + "releasedAt": "2023-04-10T16:20:33Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-648", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-649", + "isStable": false, + "releasedAt": "2023-04-10T20:08:24Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-649", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-651", + "isStable": false, + "releasedAt": "2023-04-12T20:06:56Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-651", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-653", + "isStable": false, + "releasedAt": "2023-04-13T20:10:08Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-653", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-654", + "isStable": false, + "releasedAt": "2023-04-18T03:48:14Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-654", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-655", + "isStable": false, + "releasedAt": "2023-04-18T20:09:24Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-655", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0-eap-656", + "isStable": false, + "releasedAt": "2023-04-19T20:06:19Z", + "semanticPart": "2.3.0", + "stabilityMarker": "-eap-656", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-657", + "isStable": false, + "releasedAt": "2023-04-20T20:07:50Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-657", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-658", + "isStable": false, + "releasedAt": "2023-04-21T20:08:08Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-658", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-659", + "isStable": false, + "releasedAt": "2023-04-21T20:15:09Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-659", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-660", + "isStable": false, + "releasedAt": "2023-04-25T20:05:59Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-660", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-661", + "isStable": false, + "releasedAt": "2023-04-26T20:06:38Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-661", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-662", + "isStable": false, + "releasedAt": "2023-04-27T20:06:13Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-662", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-663", + "isStable": false, + "releasedAt": "2023-04-27T20:12:49Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-663", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-664", + "isStable": false, + "releasedAt": "2023-04-28T20:07:41Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-664", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-665", + "isStable": false, + "releasedAt": "2023-04-28T20:14:24Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-665", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-666", + "isStable": false, + "releasedAt": "2023-05-01T20:06:17Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-666", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-667", + "isStable": false, + "releasedAt": "2023-05-02T12:00:28Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-667", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-668", + "isStable": false, + "releasedAt": "2023-05-02T20:06:06Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-668", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-669", + "isStable": false, + "releasedAt": "2023-05-04T20:05:48Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-669", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-670", + "isStable": false, + "releasedAt": "2023-05-05T20:06:21Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-670", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-671", + "isStable": false, + "releasedAt": "2023-05-08T20:06:38Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-671", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-672", + "isStable": false, + "releasedAt": "2023-05-10T20:06:07Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-672", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-673", + "isStable": false, + "releasedAt": "2023-05-11T20:06:13Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-673", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-674", + "isStable": false, + "releasedAt": "2023-05-12T20:07:34Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-674", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-675", + "isStable": false, + "releasedAt": "2023-05-15T20:08:29Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-675", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-676", + "isStable": false, + "releasedAt": "2023-05-16T20:07:44Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-676", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-677", + "isStable": false, + "releasedAt": "2023-05-17T20:08:02Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-677", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-678", + "isStable": false, + "releasedAt": "2023-05-19T20:06:15Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-678", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-679", + "isStable": false, + "releasedAt": "2023-05-20T20:09:51Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-679", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-680", + "isStable": false, + "releasedAt": "2023-05-23T20:07:47Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-680", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-681", + "isStable": false, + "releasedAt": "2023-05-24T20:07:49Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-681", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-682", + "isStable": false, + "releasedAt": "2023-05-25T20:08:30Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-682", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-683", + "isStable": false, + "releasedAt": "2023-05-29T20:07:45Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-683", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-684", + "isStable": false, + "releasedAt": "2023-05-31T20:07:11Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-684", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-685", + "isStable": false, + "releasedAt": "2023-05-31T20:15:08Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-685", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-686", + "isStable": false, + "releasedAt": "2023-06-01T20:07:31Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-686", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-687", + "isStable": false, + "releasedAt": "2023-06-01T20:15:53Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-687", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-688", + "isStable": false, + "releasedAt": "2023-06-02T20:07:54Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-688", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-689", + "isStable": false, + "releasedAt": "2023-06-02T20:14:46Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-689", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-690", + "isStable": false, + "releasedAt": "2023-06-05T20:07:29Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-690", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-691", + "isStable": false, + "releasedAt": "2023-06-06T20:07:44Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-691", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-692", + "isStable": false, + "releasedAt": "2023-06-07T20:09:11Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-692", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-693", + "isStable": false, + "releasedAt": "2023-06-12T20:07:50Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-693", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-694", + "isStable": false, + "releasedAt": "2023-06-12T20:15:14Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-694", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-695", + "isStable": false, + "releasedAt": "2023-06-13T20:08:01Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-695", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-696", + "isStable": false, + "releasedAt": "2023-06-20T14:57:38Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-696", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-697", + "isStable": false, + "releasedAt": "2023-06-20T20:08:28Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-697", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-698", + "isStable": false, + "releasedAt": "2023-06-20T20:17:10Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-698", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-699", + "isStable": false, + "releasedAt": "2023-06-21T20:08:40Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-699", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-700", + "isStable": false, + "releasedAt": "2023-06-23T12:49:43Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-700", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-701", + "isStable": false, + "releasedAt": "2023-06-26T20:07:48Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-701", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-702", + "isStable": false, + "releasedAt": "2023-06-27T13:20:11Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-702", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-703", + "isStable": false, + "releasedAt": "2023-06-27T20:05:57Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-703", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-705", + "isStable": false, + "releasedAt": "2023-06-28T01:35:49Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-705", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-706", + "isStable": false, + "releasedAt": "2023-06-28T14:32:49Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-706", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-707", + "isStable": false, + "releasedAt": "2023-06-28T20:05:11Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-707", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-708", + "isStable": false, + "releasedAt": "2023-06-28T20:10:59Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-708", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-709", + "isStable": false, + "releasedAt": "2023-06-29T11:05:45Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-709", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2-eap-710", + "isStable": false, + "releasedAt": "2023-06-29T20:05:16Z", + "semanticPart": "2.3.2", + "stabilityMarker": "-eap-710", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-711", + "isStable": false, + "releasedAt": "2023-06-30T13:00:56Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-711", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-712", + "isStable": false, + "releasedAt": "2023-06-30T20:05:20Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-712", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-713", + "isStable": false, + "releasedAt": "2023-07-03T20:05:28Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-713", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-714", + "isStable": false, + "releasedAt": "2023-07-05T20:05:22Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-714", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-715", + "isStable": false, + "releasedAt": "2023-07-06T06:47:23Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-715", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-716", + "isStable": false, + "releasedAt": "2023-07-06T09:15:59Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-716", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-717", + "isStable": false, + "releasedAt": "2023-07-06T20:06:28Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-717", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-718", + "isStable": false, + "releasedAt": "2023-07-06T20:11:57Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-718", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-719", + "isStable": false, + "releasedAt": "2023-07-06T20:17:43Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-719", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-720", + "isStable": false, + "releasedAt": "2023-07-07T20:04:59Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-720", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-721", + "isStable": false, + "releasedAt": "2023-07-07T20:10:34Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-721", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-722", + "isStable": false, + "releasedAt": "2023-07-08T20:07:03Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-722", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-723", + "isStable": false, + "releasedAt": "2023-07-08T20:12:47Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-723", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1-eap-724", + "isStable": false, + "releasedAt": "2023-07-10T13:00:42Z", + "semanticPart": "2.3.1", + "stabilityMarker": "-eap-724", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-725", + "isStable": false, + "releasedAt": "2023-07-10T20:05:19Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-725", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-726", + "isStable": false, + "releasedAt": "2023-07-10T20:10:57Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-726", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-727", + "isStable": false, + "releasedAt": "2023-07-12T14:42:43Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-727", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-728", + "isStable": false, + "releasedAt": "2023-07-12T20:07:07Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-728", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-730", + "isStable": false, + "releasedAt": "2023-07-13T20:05:50Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-730", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-731", + "isStable": false, + "releasedAt": "2023-07-13T20:12:29Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-731", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-732", + "isStable": false, + "releasedAt": "2023-07-13T20:18:39Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-732", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-733", + "isStable": false, + "releasedAt": "2023-07-13T20:57:23Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-733", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-734", + "isStable": false, + "releasedAt": "2023-07-14T11:16:18Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-734", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-736", + "isStable": false, + "releasedAt": "2023-07-14T20:11:20Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-736", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-737", + "isStable": false, + "releasedAt": "2023-07-17T16:48:57Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-737", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-738", + "isStable": false, + "releasedAt": "2023-07-17T17:32:14Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-738", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-739", + "isStable": false, + "releasedAt": "2023-07-17T20:07:09Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-739", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-740", + "isStable": false, + "releasedAt": "2023-07-18T20:05:23Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-740", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-741", + "isStable": false, + "releasedAt": "2023-07-18T20:10:48Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-741", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-751", + "isStable": false, + "releasedAt": "2023-07-26T10:57:14Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-751", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-756", + "isStable": false, + "releasedAt": "2023-07-31T07:13:14Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-756", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-757", + "isStable": false, + "releasedAt": "2023-07-31T09:09:50Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-757", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-758", + "isStable": false, + "releasedAt": "2023-07-31T20:07:13Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-758", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-759", + "isStable": false, + "releasedAt": "2023-07-31T20:13:40Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-759", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-761", + "isStable": false, + "releasedAt": "2023-08-01T20:05:49Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-761", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-762", + "isStable": false, + "releasedAt": "2023-08-01T20:12:20Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-762", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-763", + "isStable": false, + "releasedAt": "2023-08-02T20:05:15Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-763", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-764", + "isStable": false, + "releasedAt": "2023-08-04T20:05:26Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-764", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-765", + "isStable": false, + "releasedAt": "2023-08-07T20:05:45Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-765", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-766", + "isStable": false, + "releasedAt": "2023-08-08T20:05:07Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-766", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-767", + "isStable": false, + "releasedAt": "2023-08-09T20:05:20Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-767", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-768", + "isStable": false, + "releasedAt": "2023-08-10T20:06:31Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-768", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-769", + "isStable": false, + "releasedAt": "2023-08-11T20:06:49Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-769", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-770", + "isStable": false, + "releasedAt": "2023-08-14T20:05:42Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-770", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-771", + "isStable": false, + "releasedAt": "2023-08-16T20:06:08Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-771", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-772", + "isStable": false, + "releasedAt": "2023-08-17T20:07:09Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-772", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-773", + "isStable": false, + "releasedAt": "2023-08-19T20:07:33Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-773", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-774", + "isStable": false, + "releasedAt": "2023-08-20T20:06:59Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-774", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-775", + "isStable": false, + "releasedAt": "2023-08-21T20:06:04Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-775", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-776", + "isStable": false, + "releasedAt": "2023-08-21T20:11:41Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-776", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-777", + "isStable": false, + "releasedAt": "2023-08-22T20:06:05Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-777", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-778", + "isStable": false, + "releasedAt": "2023-08-22T20:13:25Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-778", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-779", + "isStable": false, + "releasedAt": "2023-08-28T20:06:25Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-779", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-780", + "isStable": false, + "releasedAt": "2023-08-30T20:08:51Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-780", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3-eap-781", + "isStable": false, + "releasedAt": "2023-08-30T20:14:14Z", + "semanticPart": "2.3.3", + "stabilityMarker": "-eap-781", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-784", + "isStable": false, + "releasedAt": "2023-09-01T20:07:09Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-784", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-785", + "isStable": false, + "releasedAt": "2023-09-01T20:12:53Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-785", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-786", + "isStable": false, + "releasedAt": "2023-09-02T18:46:33Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-786", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-787", + "isStable": false, + "releasedAt": "2023-09-04T11:21:45Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-787", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-788", + "isStable": false, + "releasedAt": "2023-09-04T16:19:32Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-788", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-789", + "isStable": false, + "releasedAt": "2023-09-04T20:05:37Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-789", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-790", + "isStable": false, + "releasedAt": "2023-09-05T16:12:23Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-790", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-791", + "isStable": false, + "releasedAt": "2023-09-05T20:05:49Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-791", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-792", + "isStable": false, + "releasedAt": "2023-09-07T15:06:33Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-792", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-793", + "isStable": false, + "releasedAt": "2023-09-07T20:06:57Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-793", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-794", + "isStable": false, + "releasedAt": "2023-09-07T20:12:39Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-794", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-795", + "isStable": false, + "releasedAt": "2023-09-11T20:05:50Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-795", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-796", + "isStable": false, + "releasedAt": "2023-09-11T20:12:02Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-796", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-797", + "isStable": false, + "releasedAt": "2023-09-11T21:49:39Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-797", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-799", + "isStable": false, + "releasedAt": "2023-09-11T22:04:28Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-799", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-800", + "isStable": false, + "releasedAt": "2023-09-12T05:41:57Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-800", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-801", + "isStable": false, + "releasedAt": "2023-09-18T20:05:39Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-801", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-802", + "isStable": false, + "releasedAt": "2023-09-20T20:06:48Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-802", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-803", + "isStable": false, + "releasedAt": "2023-09-21T20:07:10Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-803", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-804", + "isStable": false, + "releasedAt": "2023-09-22T20:06:49Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-804", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-805", + "isStable": false, + "releasedAt": "2023-09-25T20:06:58Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-805", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-806", + "isStable": false, + "releasedAt": "2023-09-26T20:07:08Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-806", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-807", + "isStable": false, + "releasedAt": "2023-09-28T20:06:51Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-807", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-808", + "isStable": false, + "releasedAt": "2023-09-29T20:06:24Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-808", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-809", + "isStable": false, + "releasedAt": "2023-10-02T13:45:01Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-809", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-810", + "isStable": false, + "releasedAt": "2023-10-02T20:06:57Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-810", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-813", + "isStable": false, + "releasedAt": "2023-10-04T20:07:15Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-813", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-815", + "isStable": false, + "releasedAt": "2023-10-06T20:06:20Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-815", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-816", + "isStable": false, + "releasedAt": "2023-10-10T09:37:57Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-816", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-817", + "isStable": false, + "releasedAt": "2023-10-10T20:07:53Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-817", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-818", + "isStable": false, + "releasedAt": "2023-10-12T20:06:37Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-818", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-819", + "isStable": false, + "releasedAt": "2023-10-16T20:06:08Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-819", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-820", + "isStable": false, + "releasedAt": "2023-10-16T20:29:11Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-820", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-821", + "isStable": false, + "releasedAt": "2023-10-16T21:11:01Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-821", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-822", + "isStable": false, + "releasedAt": "2023-10-17T07:47:59Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-822", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-823", + "isStable": false, + "releasedAt": "2023-10-17T13:30:08Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-823", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-824", + "isStable": false, + "releasedAt": "2023-10-17T20:07:57Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-824", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-825", + "isStable": false, + "releasedAt": "2023-10-17T20:15:01Z", + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-825", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.0.0-rc", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.0.0", + "stabilityMarker": "-rc", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.0.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.0.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.0.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.0.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.1.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.1.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.1.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.1.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.1.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.1.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.1.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.1.4", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.1.4", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.1.5", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.1.5", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.0-rc", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.2.0", + "stabilityMarker": "-rc", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.0-rc2", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.2.0", + "stabilityMarker": "-rc2", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.3-rc", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.2.3", + "stabilityMarker": "-rc", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.4", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.4", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.5", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.5", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.2.6", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.2.6", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.3.0-rc", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.3.0", + "stabilityMarker": "-rc", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.3.0-rc2", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.3.0", + "stabilityMarker": "-rc2", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.3.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.3.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.3.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.3.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.3.2-1.4.0-rc", + "isStable": false, + "releasedAt": null, + "semanticPart": "1.3.2", + "stabilityMarker": null, + "nonSemanticSuffix": "-1.4.0-rc" + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.4.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.4.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.4.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.4.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.4.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.4.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.4.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.4.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.5.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.5.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.5.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.5.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.5.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.5.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.5.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.5.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.5.4", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.5.4", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.4", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.4", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.5", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.5", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.6", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.6", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.7", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.7", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "1.6.8", + "isStable": true, + "releasedAt": null, + "semanticPart": "1.6.8", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-beta-1", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.0.0", + "stabilityMarker": "-beta-1", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0-rc-1", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.0.0", + "stabilityMarker": "-rc-1", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.0.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.0.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.0.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.0.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.0.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.1.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.1.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.1.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.1.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.1.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.2.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.2.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.2.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.2.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.2.4", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.2.4", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.0", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.0", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.1", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.1", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.2", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.2", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.3", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.3", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.4", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.4", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.5", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.5", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.6", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.6", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.7", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.7", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-826", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-826", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-827", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-827", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-828", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-828", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-829", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-829", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-830", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-830", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-831", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-831", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-832", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-832", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.6-eap-833", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.3.6", + "stabilityMarker": "-eap-833", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.6-eap-834", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.3.6", + "stabilityMarker": "-eap-834", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-835", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-835", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.6-eap-836", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.3.6", + "stabilityMarker": "-eap-836", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-837", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-837", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.6-eap-838", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.3.6", + "stabilityMarker": "-eap-838", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-839", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-839", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-840", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-840", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-841", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-841", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-842", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-842", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-843", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-843", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-844", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-844", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-845", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-845", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-846", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-846", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-847", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-847", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-848", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-848", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-849", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-849", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-850", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-850", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-851", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-851", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-eap-852", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-eap-852", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-853", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-853", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-854", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-854", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-856", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-856", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.7-eap-857", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.3.7", + "stabilityMarker": "-eap-857", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-858", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-858", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-859", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-859", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-860", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-860", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-861", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-861", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.7-eap-862", + "isStable": false, + "releasedAt": null, + "semanticPart": "2.3.7", + "stabilityMarker": "-eap-862", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-863", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-863", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-864", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-864", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-865", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-865", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-866", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-866", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-867", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-867", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-868", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-868", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-869", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-869", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-870", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-870", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-871", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-871", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-872", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-872", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-873", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-873", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-874", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-874", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-875", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-875", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-876", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-876", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-877", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-877", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-878", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-878", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-879", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-879", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-880", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-880", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-881", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-881", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-2-eap-884", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-2-eap-884", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-885", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-885", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-886", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-886", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-888", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-888", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "3.0.0-beta-1-eap-889", + "isStable": false, + "releasedAt": null, + "semanticPart": "3.0.0", + "stabilityMarker": "-beta-1-eap-889", + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "ktor_eap" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.9", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.9", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + }, + { + "normalizedVersion": { + "type": "semantic", + "versionName": "2.3.11", + "isStable": true, + "releasedAt": null, + "semanticPart": "2.3.11", + "stabilityMarker": null, + "nonSemanticSuffix": null + }, + "repositoryIds": [ + "maven_central" + ] + } + ] + }, + "licenses": { + "mainLicense": { + "name": "The Apache Software License, Version 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.txt" + }, + "otherLicenses": [] + }, + "groupId": "io.ktor", + "artifactId": "ktor-client-core", + "scm": { + "type": "github", + "url": "https://github.com/ktorio/ktor.git", + "urlHash": "6f0779e7fd7cac97e2186145d417bfa9f934234e53d7fc8355634a51d3ef811d", + "description": "Framework for quickly creating connected applications in Kotlin with minimal effort", + "readmeUrl": "https://raw.githubusercontent.com/ktorio/ktor/main/README.md", + "license": { + "name": "Apache License 2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "htmlUrl": "https://github.com/ktorio/ktor/blob/main/LICENSE", + "spdxId": "Apache-2.0", + "key": "apache-2.0" + }, + "htmlUrl": "https://github.com/ktorio/ktor", + "isFork": false, + "ossHealthIndex": 0.5113194418722041, + "stars": 12003, + "watchers": 12003, + "forks": 992, + "subscribers": 177, + "readme": { + "rawUrl": "https://raw.githubusercontent.com/ktorio/ktor/main/README.md", + "htmlUrl": "https://github.com/ktorio/ktor/blob/main/README.md" + } + } + } +] \ No newline at end of file diff --git a/packagesearch-api-models.versions.toml b/packagesearch-api-models.versions.toml index 7ab6db6..b3d2c24 100644 --- a/packagesearch-api-models.versions.toml +++ b/packagesearch-api-models.versions.toml @@ -11,6 +11,7 @@ kotlinx-serialization = "1.6.3" krypto = "4.0.10" ktor = "2.3.10" logback = "1.5.6" +mvstore = "2.3.230" xmlutil = "0.86.2" # DO NOT UPDATE TO 0.83.3, it breaks org.jetbrains.packagesearch.maven.XML#decodeFromString [libraries] @@ -36,10 +37,12 @@ ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "kto ktor-client-encoding = { module = "io.ktor:ktor-client-encoding", version.ref = "ktor" } ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } +ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } ktor-http = { module = "io.ktor:ktor-http", version.ref = "ktor" } ktor-serialization-kotlinx = { module = "io.ktor:ktor-serialization-kotlinx", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } ktor-serialization-kotlinx-protobuf = { module = "io.ktor:ktor-serialization-kotlinx-protobuf", version.ref = "ktor" } ktor-serialization-kotlinx-xml = { module = "io.ktor:ktor-serialization-kotlinx-xml", version.ref = "ktor" } logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } +mvstore = { module = "com.h2database:h2-mvstore", version.ref = "mvstore" } xmlutil = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "xmlutil" } From 4e12aeb97406ac910349c9bb7f869e8ba758a905 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Mon, 22 Jul 2024 10:10:07 +0200 Subject: [PATCH 06/27] Refactor http client and mock engine setup in CacheTests Abstracted the creation of the mock engine and HTTP client setup to separate helper functions. This reduces redundancy in the test code and improves maintainability. Added test cases for caching logic involving known repositories and package ID hashes. --- .../api/v3/http/PackageSearchApiClient.kt | 2 +- .../src/commonTest/kotlin/CacheTests.kt | 146 +++++++++--------- http/client/src/commonTest/kotlin/Utils.kt | 65 ++++++++ .../resources/known-repositories.json | 89 +++++++++++ 4 files changed, 231 insertions(+), 71 deletions(-) create mode 100644 http/client/src/commonTest/resources/known-repositories.json diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 631749e..44ea638 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -181,7 +181,7 @@ public class PackageSearchApiClient( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - fetchPackageInfo(ids, "idHash", endpoints.packageInfoByIds, requestBuilder) + fetchPackageInfo(ids, "idHash", endpoints.packageInfoByIdHashes, requestBuilder) // Common Function to Fetch Package Info (Handles Both ID and ID Hash Retrieval) private suspend fun fetchPackageInfo( diff --git a/http/client/src/commonTest/kotlin/CacheTests.kt b/http/client/src/commonTest/kotlin/CacheTests.kt index 9880ab5..e69e077 100644 --- a/http/client/src/commonTest/kotlin/CacheTests.kt +++ b/http/client/src/commonTest/kotlin/CacheTests.kt @@ -1,23 +1,10 @@ import cache.ApiPackageCacheEntry import cache.CacheDB -import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine -import io.ktor.client.engine.mock.respond -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.logging.DEFAULT -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger -import io.ktor.client.plugins.logging.Logging -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpStatusCode -import io.ktor.http.headers -import io.ktor.serialization.kotlinx.json.json import kotlinx.coroutines.test.runTest import kotlinx.datetime.Clock -import kotlinx.document.database.mvstore.asDataStore -import kotlinx.serialization.json.Json -import org.h2.mvstore.MVStore import org.jetbrains.packagesearch.api.v3.ApiPackage +import org.jetbrains.packagesearch.api.v3.ApiRepository import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints import org.junit.jupiter.api.Test @@ -28,29 +15,29 @@ import kotlin.time.Duration.Companion.seconds class CacheTests { + internal data class TestEnv( + val apiClient: PackageSearchApiClient, + val mockEngine: MockEngine, + val mockResponse: T, + val db: CacheDB + ) @Test - fun testCacheHit() = runTest(timeout = 10.seconds) { + fun `PackageByID test Cache Hit`() = runTest(timeout = 10.seconds) { - val jsonResponse = this::class.java.classLoader.getResource("package-info-by-ids-ktor.json")!!.readText() - val mockResponse = Json.decodeFromString>(jsonResponse).first() - - val mockEngine = buildMockEngine(jsonResponse) - - // ApiClient coroutine scope will be created from httpClient coroutine context - val apiClient = PackageSearchApiClient( - httpClient = setupHttpClient(mockEngine), - endpoints = PackageSearchEndpoints.DEV, - dataStore = MVStore.open(null).asDataStore(), //in memory db - ) + val (apiClient, mockEngine, mockResponse) = + setupTestEnv>( + mockResponseResourceName = "package-info-by-ids-ktor.json", + ) + val packageId = mockResponse.first().id // First request (populates cache) - val response1 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) - assert(response1.values.firstOrNull()!!.id == mockResponse.id) + val response1 = apiClient.getPackageInfoByIds(setOf(packageId)) + assert(response1.values.firstOrNull()!!.id == packageId) // Second request (should hit cache) - val response2 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) - assert(response2.values.firstOrNull()!!.id == mockResponse.id) + val response2 = apiClient.getPackageInfoByIds(setOf(packageId)) + assert(response2.values.firstOrNull()!!.id == packageId) // Ensure the mock engine wasn't called a second time. val endpointsCalls = @@ -62,65 +49,84 @@ class CacheTests { @Test - fun testCacheRefresh() = runTest(timeout = 20.seconds) { - val jsonResponse = this::class.java.classLoader.getResource("package-info-by-ids-ktor.json")!!.readText() - val mockResponse = Json.decodeFromString>(jsonResponse).first() + fun `PackageByID test Cache Refresh`() = runTest(timeout = 20.seconds) { + + val testEnv = setupTestEnv>( + mockResponseResourceName = "package-info-by-ids-ktor.json", + ) val expiredDBEntry = ApiPackageCacheEntry( - apiPackage = mockResponse, + apiPackage = testEnv.mockResponse.first(), expires = Clock.System.now().minus(1.minutes), ) - val dataStore = MVStore.open(null).asDataStore() - //setting up db - val collection = CacheDB(dataStore).apiPackagesCache() - collection.insert(expiredDBEntry) - - val mockEngine = buildMockEngine(jsonResponse) - - // ApiClient coroutine scope will be created from httpClient coroutine context - val apiClient = PackageSearchApiClient( - httpClient = setupHttpClient(mockEngine), - endpoints = PackageSearchEndpoints.DEV, - dataStore = dataStore, - ) + testEnv.db.apiPackagesCache().insert(expiredDBEntry) + val packageId = testEnv.mockResponse.first().id // First request (should refresh cache) - val response1 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) - assert(response1.values.firstOrNull()!!.id == mockResponse.id) + val response1 = testEnv.apiClient.getPackageInfoByIds(setOf(packageId)) + assert(response1.values.firstOrNull()!!.id == packageId) // Ensure data has been retrieved from BE. - assertEquals(1, mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) + assertEquals(1, testEnv.mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) // Second request (should hit cache) - val response2 = apiClient.getPackageInfoByIds(setOf(mockResponse.id)) - assert(response2.values.firstOrNull()!!.id == mockResponse.id) + val response2 = testEnv.apiClient.getPackageInfoByIds(setOf(packageId)) + assert(response2.values.firstOrNull()!!.id == packageId) - mockEngine.close() - assertEquals(1, mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) + testEnv.mockEngine.close() + assertEquals(1, testEnv.mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) } - private fun setupHttpClient(mockEngine: MockEngine): HttpClient = HttpClient(mockEngine) { - install(ContentNegotiation) { - json() - } - install(Logging) { - logger = Logger.DEFAULT - level = LogLevel.HEADERS - } + @Test + fun `PackageByHash test Cache Hit`() = runTest(timeout = 10.seconds) { + val (apiClient, mockEngine, mockResponse) = setupTestEnv>( + mockResponseResourceName = "package-info-by-ids-ktor.json", + ) + + val packageIdHash = mockResponse.first().idHash + // First request (populates cache) + val response1 = apiClient.getPackageInfoByIdHashes(setOf(packageIdHash)) + assert(response1.values.firstOrNull()!!.idHash == packageIdHash) + + // Second request (should hit cache) + val response2 = apiClient.getPackageInfoByIdHashes(setOf(packageIdHash)) + assert(response2.values.firstOrNull()!!.idHash == packageIdHash) + + // Ensure the mock engine wasn't called a second time. + val endpointsCalls = + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIdHashes) + + mockEngine.close() + assertEquals(1, endpointsCalls) } - private fun buildMockEngine(jsonResponse: String): MockEngine = MockEngine { - respond( - content = jsonResponse, - status = HttpStatusCode.OK, - headers = headers { - append(HttpHeaders.ContentType, "application/json") - } + @Test + fun `getKnownRepositories test Cache Hit`() = runTest(timeout = 10.seconds) { + val (apiClient, mockEngine ) = setupTestEnv>("known-repositories.json") + + val response1 = apiClient.getKnownRepositories() + // Second request (should hit cache) + val response2 = apiClient.getKnownRepositories() - ) + response1.map { it.id }.forEach(){ + assert(it in response2.map { it.id }) + } + + // Ensure the mock engine wasn't called a second time. + val endpointsCalls = + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.knownRepositories) + + mockEngine.close() + assertEquals(1, endpointsCalls) + + } + + @Test + fun `searchPackages test Cache Hit`() = runTest(timeout = 10.seconds) { + //todo } diff --git a/http/client/src/commonTest/kotlin/Utils.kt b/http/client/src/commonTest/kotlin/Utils.kt index 669eefa..aeeb280 100644 --- a/http/client/src/commonTest/kotlin/Utils.kt +++ b/http/client/src/commonTest/kotlin/Utils.kt @@ -1,6 +1,71 @@ +import CacheTests.TestEnv +import cache.CacheDB +import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.logging.DEFAULT +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode import io.ktor.http.Url +import io.ktor.http.headers +import io.ktor.serialization.kotlinx.json.json +import kotlinx.coroutines.test.TestScope +import kotlinx.document.database.mvstore.asDataStore +import kotlinx.serialization.json.Json +import org.h2.mvstore.MVStore +import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient +import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints fun MockEngine.getRequestCount(filterUrl: Url) = requestHistory.filter { it.url == filterUrl }.size + + +internal inline fun TestScope.setupTestEnv( + mockResponseResourceName: String, +): TestEnv { + val jsonResponse = this::class.java.classLoader.getResource(mockResponseResourceName)!!.readText() + val mockResponse = Json.decodeFromString(jsonResponse) + + val mockEngine = buildMockEngine(jsonResponse) + + val dataStore = MVStore.open(null).asDataStore() + + val apiClient = PackageSearchApiClient( + httpClient = setupHttpClient(mockEngine), + endpoints = PackageSearchEndpoints.DEV, + dataStore = MVStore.open(null).asDataStore(), //in memory db + ) + + return TestEnv( + apiClient = apiClient, + mockEngine = mockEngine, + mockResponse = mockResponse, + db = CacheDB(dataStore) + ) +} + +private fun setupHttpClient(mockEngine: MockEngine): HttpClient = HttpClient(mockEngine) { + install(ContentNegotiation) { + json() + } + install(Logging) { + logger = Logger.DEFAULT + level = LogLevel.HEADERS + } +} + +private fun buildMockEngine(jsonResponse: String): MockEngine = MockEngine { + respond( + content = jsonResponse, + status = HttpStatusCode.OK, + headers = headers { + append(HttpHeaders.ContentType, "application/json") + } + + ) +} \ No newline at end of file diff --git a/http/client/src/commonTest/resources/known-repositories.json b/http/client/src/commonTest/resources/known-repositories.json new file mode 100644 index 0000000..e8c0efb --- /dev/null +++ b/http/client/src/commonTest/resources/known-repositories.json @@ -0,0 +1,89 @@ +[ + { + "type": "maven", + "id": "maven_central", + "lastChecked": null, + "url": "https://repo.maven.apache.org/maven2/", + "alternateUrls": [ + "https://repo1.maven.org/maven2/", + "https://maven-central.storage-download.googleapis.com/maven2" + ], + "friendlyName": "Maven Central", + "userFacingUrl": "https://search.maven.org/", + "packageCount": null, + "artifactCount": null, + "namedLinks": null + }, + { + "type": "maven", + "id": "clojars", + "lastChecked": null, + "url": "https://repo.clojars.org/", + "alternateUrls": [], + "friendlyName": "Maven Central", + "userFacingUrl": "https://clojars.org/", + "packageCount": null, + "artifactCount": null, + "namedLinks": null + }, + { + "type": "maven", + "id": "ktor_eap", + "lastChecked": null, + "url": "https://maven.pkg.jetbrains.space/public/p/ktor/eap/", + "alternateUrls": [], + "friendlyName": "Ktor EAP", + "userFacingUrl": null, + "packageCount": null, + "artifactCount": null, + "namedLinks": null + }, + { + "type": "maven", + "id": "dokka_dev", + "lastChecked": null, + "url": "https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev/", + "alternateUrls": [], + "friendlyName": "Dokka DEV", + "userFacingUrl": null, + "packageCount": null, + "artifactCount": null, + "namedLinks": null + }, + { + "type": "maven", + "id": "compose_dev", + "lastChecked": null, + "url": "https://maven.pkg.jetbrains.space/public/p/compose/dev/", + "alternateUrls": [], + "friendlyName": "Compose DEV", + "userFacingUrl": null, + "packageCount": null, + "artifactCount": null, + "namedLinks": null + }, + { + "type": "maven", + "id": "space_sdk", + "lastChecked": null, + "url": "https://maven.pkg.jetbrains.space/public/p/space/maven/", + "alternateUrls": [], + "friendlyName": "Space SDK", + "userFacingUrl": null, + "packageCount": null, + "artifactCount": null, + "namedLinks": null + }, + { + "type": "maven", + "id": "gmaven", + "lastChecked": null, + "url": "https://maven.google.com/", + "alternateUrls": [], + "friendlyName": "Google Maven", + "userFacingUrl": null, + "packageCount": null, + "artifactCount": null, + "namedLinks": null + } +] \ No newline at end of file From 0cc271a7a34d341b694bcd6eb12a700fc39e4453 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Mon, 22 Jul 2024 11:35:23 +0200 Subject: [PATCH 07/27] Add cache behavior tests and refactor setupTestEnv parameter Introduce multiple tests to verify caching mechanisms including cache hits, cache expiration, and cache non-exhaustiveness. Also, refactor the 'setupTestEnv' function to use 'resourceFilename' parameter instead of 'mockResponseResourceName' for clarity and consistency. --- .../src/commonTest/kotlin/CacheTests.kt | 108 ++++++++++++++++-- http/client/src/commonTest/kotlin/Utils.kt | 9 +- 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/http/client/src/commonTest/kotlin/CacheTests.kt b/http/client/src/commonTest/kotlin/CacheTests.kt index e69e077..624a943 100644 --- a/http/client/src/commonTest/kotlin/CacheTests.kt +++ b/http/client/src/commonTest/kotlin/CacheTests.kt @@ -7,6 +7,9 @@ import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiRepository import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints +import org.jetbrains.packagesearch.api.v3.http.RefreshPackagesInfoRequest +import org.jetbrains.packagesearch.api.v3.http.SearchPackagesRequest +import org.jetbrains.packagesearch.api.v3.search.PackagesType import org.junit.jupiter.api.Test import kotlin.collections.first import kotlin.test.assertEquals @@ -26,9 +29,7 @@ class CacheTests { fun `PackageByID test Cache Hit`() = runTest(timeout = 10.seconds) { val (apiClient, mockEngine, mockResponse) = - setupTestEnv>( - mockResponseResourceName = "package-info-by-ids-ktor.json", - ) + setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") val packageId = mockResponse.first().id // First request (populates cache) @@ -49,11 +50,10 @@ class CacheTests { @Test - fun `PackageByID test Cache Refresh`() = runTest(timeout = 20.seconds) { + fun `PackageByID test Cache Expired`() = runTest(timeout = 20.seconds) { - val testEnv = setupTestEnv>( - mockResponseResourceName = "package-info-by-ids-ktor.json", - ) + val testEnv = + setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") val expiredDBEntry = ApiPackageCacheEntry( apiPackage = testEnv.mockResponse.first(), @@ -82,9 +82,8 @@ class CacheTests { @Test fun `PackageByHash test Cache Hit`() = runTest(timeout = 10.seconds) { - val (apiClient, mockEngine, mockResponse) = setupTestEnv>( - mockResponseResourceName = "package-info-by-ids-ktor.json", - ) + val (apiClient, mockEngine, mockResponse) = + setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") val packageIdHash = mockResponse.first().idHash // First request (populates cache) @@ -103,6 +102,22 @@ class CacheTests { assertEquals(1, endpointsCalls) } + @Test + fun `PackageById test cache not exhaustive`() = runTest(timeout = 10.seconds) { + val (apiClient, mockEngine, mockResponse) = + setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") + //create cache entry + apiClient.getPackageInfoByIds(mockResponse.map { it.id }.toSet()) + + apiClient.getPackageInfoByIds(mockResponse.map { it.id }.toSet() + "androidx.compose.runtime:runtime") + + val endpointsCalls = + mockEngine.geRequestsFor(PackageSearchEndpoints.DEV.packageInfoByIds) + + mockEngine.close() + assertEquals(2, endpointsCalls.size) + } + @Test fun `getKnownRepositories test Cache Hit`() = runTest(timeout = 10.seconds) { val (apiClient, mockEngine ) = setupTestEnv>("known-repositories.json") @@ -111,7 +126,7 @@ class CacheTests { // Second request (should hit cache) val response2 = apiClient.getKnownRepositories() - response1.map { it.id }.forEach(){ + response1.map { it.id }.forEach() { assert(it in response2.map { it.id }) } @@ -126,7 +141,76 @@ class CacheTests { @Test fun `searchPackages test Cache Hit`() = runTest(timeout = 10.seconds) { - //todo + val (apiClient, mockEngine) = setupTestEnv>("package-info-by-ids-ktor.json") + val queryString = "whatever" + repeat(3) { + apiClient.searchPackages( + SearchPackagesRequest( + listOf(PackagesType.Maven), + searchQuery = queryString + ) + ) + } + val endpointsCalls = + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.searchPackages) + + mockEngine.close() + assertEquals(1, endpointsCalls) + + } + + @Test + fun `searchPackages non exhaustive cache`() = runTest(timeout = 10.seconds) { + val (apiClient, mockEngine) = setupTestEnv>("package-info-by-ids-ktor.json") + val queryString = "whatever" + apiClient.searchPackages( + SearchPackagesRequest( + listOf(PackagesType.Maven), + searchQuery = queryString + ) + ) + apiClient.searchPackages( + SearchPackagesRequest( + listOf(PackagesType.Maven, PackagesType.Npm), + searchQuery = queryString + ) + ) + + val endpointsCalls = + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.searchPackages) + + mockEngine.close() + assertEquals(2, endpointsCalls) + + } + + @Test + fun `refreshPackagesInfo smart cache`() = runTest(timeout = 10.seconds) { + val (apiClient, mockEngine, mockResponse) = setupTestEnv>("package-info-by-ids-ktor.json") + + val refreshRequest = RefreshPackagesInfoRequest( + listOf( + RefreshPackagesInfoRequest.CacheRequest( + mockResponse.first().idHash + ) + ) + ) + + //ask to refresh package info for ktor hashID -> a new cache entry should be created or updated + apiClient.refreshPackagesInfo(refreshRequest) + + //Note: delay will be skipped by Jupiter runTest function + //we need to wait a small amount of time before we will find the cache updated + Thread.sleep(500) + + apiClient.getPackageInfoByIds(setOf(mockResponse.first().id)) + + val endpointsCalls = + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds) + + mockEngine.close() + assertEquals(0, endpointsCalls) + } diff --git a/http/client/src/commonTest/kotlin/Utils.kt b/http/client/src/commonTest/kotlin/Utils.kt index aeeb280..168c9b0 100644 --- a/http/client/src/commonTest/kotlin/Utils.kt +++ b/http/client/src/commonTest/kotlin/Utils.kt @@ -21,14 +21,17 @@ import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints +fun MockEngine.geRequestsFor(url: Url) = + requestHistory.filter { it.url == url } + fun MockEngine.getRequestCount(filterUrl: Url) = - requestHistory.filter { it.url == filterUrl }.size + geRequestsFor(filterUrl).size internal inline fun TestScope.setupTestEnv( - mockResponseResourceName: String, + resourceFilename: String, ): TestEnv { - val jsonResponse = this::class.java.classLoader.getResource(mockResponseResourceName)!!.readText() + val jsonResponse = this::class.java.classLoader.getResource(resourceFilename)!!.readText() val mockResponse = Json.decodeFromString(jsonResponse) val mockEngine = buildMockEngine(jsonResponse) From 3582027a5205c5bce3d3adecac2c67aee974ff5f Mon Sep 17 00:00:00 2001 From: fscarponi Date: Mon, 22 Jul 2024 11:42:19 +0200 Subject: [PATCH 08/27] Remove unused functions isOnline and isOffline The functions isOnline and isOffline were not being used anywhere in the code. Cleaning up these unused functions improves code maintainability and readability. --- .../org/jetbrains/packagesearch/api/v3/http/Utils.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt index 8ecc2d7..9293863 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/Utils.kt @@ -1,6 +1,5 @@ package org.jetbrains.packagesearch.api.v3.http -import io.ktor.client.* import io.ktor.client.plugins.* import io.ktor.client.request.* import io.ktor.http.* @@ -49,10 +48,3 @@ public data class SerializableHttpRequest( public fun HttpRequest.toSerializable(): SerializableHttpRequest = SerializableHttpRequest(url.toString(), method.value, headers.toMap()) - -internal suspend fun HttpClient.isOffline() = !isOnline() - -internal suspend fun HttpClient.isOnline() = - head("https://www.jetbrains.com").status.isSuccess() - - From 6490050c233264ed08689e3b84044111f187d06e Mon Sep 17 00:00:00 2001 From: fscarponi Date: Mon, 22 Jul 2024 17:17:27 +0200 Subject: [PATCH 09/27] Refactor fetchPackageInfo and reorganize dependencies Refactor fetchPackageInfo to handle idHash logic and streamline function calls. Additionally, reorganize test dependencies and Kotlin configurations, including updating the JVM toolchain to version 17. --- build.gradle.kts | 3 +- .../src/main/kotlin/build-config.gradle.kts | 21 ++++---------- http/client/build.gradle.kts | 11 ++++++-- .../api/v3/http/PackageSearchApiClient.kt | 28 ++++++++++++------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a23faf8..ec624c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ kotlin { jsMain { dependencies { api(npm(packageSearchApiModelsVersions.date.fns)) - api(kotlinxDocumentStore.mvstore) + api(kotlinxDocumentStore.browser) } } jvmTest { @@ -25,7 +25,6 @@ kotlin { implementation(packageSearchApiModelsVersions.junit.jupiter.params) implementation(packageSearchApiModelsVersions.assertk) implementation(kotlinxDocumentStore.mvstore) - runtimeOnly(packageSearchApiModelsVersions.junit.jupiter.engine) } } diff --git a/buildSrc/src/main/kotlin/build-config.gradle.kts b/buildSrc/src/main/kotlin/build-config.gradle.kts index 0069809..4e7fc2a 100644 --- a/buildSrc/src/main/kotlin/build-config.gradle.kts +++ b/buildSrc/src/main/kotlin/build-config.gradle.kts @@ -13,23 +13,12 @@ plugins { kotlin { explicitApi() - jvm { -// jvmToolchain(11) + jvm() + jvmToolchain(17) + js(IR) { + browser() + nodejs() } -// js(IR) { -// browser() -// nodejs() -// } -// iosX64() -// iosArm64() -// iosSimulatorArm64() -// macosArm64() -// macosX64() -// watchosArm64() -// watchosX64() -// tvosArm64() -// tvosX64() -// tvosSimulatorArm64() targets.all { compilations.all { kotlinOptions { diff --git a/http/client/build.gradle.kts b/http/client/build.gradle.kts index 66bbfc1..740737a 100644 --- a/http/client/build.gradle.kts +++ b/http/client/build.gradle.kts @@ -27,13 +27,10 @@ kotlin { } commonTest{ dependencies{ - api(kotlin("test-junit5")) api(packageSearchApiModelsVersions.kotlinx.coroutines.test) api(packageSearchApiModelsVersions.ktor.client.mock) api(packageSearchApiModelsVersions.ktor.client.logging) - api(packageSearchApiModelsVersions.junit.jupiter.engine) api(packageSearchApiModelsVersions.mvstore) - api(kotlinxDocumentStore.mvstore) } } jvmMain { @@ -41,5 +38,13 @@ kotlin { api(packageSearchApiModelsVersions.logback.classic) } } + jvmTest { + dependencies { + api(kotlin("test-junit5")) + api(kotlinxDocumentStore.mvstore) + api(packageSearchApiModelsVersions.junit.jupiter.engine) + + } + } } } \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 44ea638..3f2d5fe 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -175,27 +175,36 @@ public class PackageSearchApiClient( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - fetchPackageInfo(ids, "id", endpoints.packageInfoByIds, requestBuilder) + fetchPackageInfo( + ids = ids, + requestBuilder = requestBuilder) public suspend fun getPackageInfoByIdHashes( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - fetchPackageInfo(ids, "idHash", endpoints.packageInfoByIdHashes, requestBuilder) + fetchPackageInfo( + ids = ids, + idHash = true, + requestBuilder = requestBuilder + ) // Common Function to Fetch Package Info (Handles Both ID and ID Hash Retrieval) private suspend fun fetchPackageInfo( - identifiers: Set, - lookupField: String, // "idHash" or "id" - endpointUrl: Url, // Differentiate between the two endpoints + ids: Set, + idHash: Boolean = false, // "idHash" or "id" requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = coroutineScope { val apiPackageCacheDB = cacheDB.apiPackagesCache() val isOffline = !onlineStateFlow.value + val lookupField = if (idHash) "idHash" else "id" + val endpointUrl = + if (idHash) endpoints.packageInfoByIdHashes else endpoints.packageInfoByIds + val cachedResults = - identifiers + ids .map { id -> // NOTE: id depends on the lookupField async { apiPackageCacheDB @@ -210,7 +219,7 @@ public class PackageSearchApiClient( val validResults = if (!isOffline) cachedResults.filter { !it.value.isExpired } else cachedResults - val unresolvedIdentifiers = identifiers - validResults.keys + val unresolvedIdentifiers = ids - validResults.keys if (unresolvedIdentifiers.isEmpty()) { return@coroutineScope validResults.mapValues { it.value.apiPackage } @@ -224,7 +233,6 @@ public class PackageSearchApiClient( ).associateBy { if (lookupField == "id") it.id else it.idHash } - onlineResults.values.forEach { val cacheKey = if (lookupField == "id") it.id else it.idHash @@ -350,7 +358,7 @@ public class PackageSearchApiClient( ) // update ApiPackageCache - coroutineScope.launch(Dispatchers.IO) { + coroutineScope.launch { val apiPackageCacheDB = cacheDB.apiPackagesCache() val updates = results @@ -392,7 +400,7 @@ public class PackageSearchApiClient( } private fun initiateOnlineCheckJob(onlineCheckInterval: Duration) { - coroutineScope.launch(Dispatchers.IO) { + coroutineScope.launch { while (coroutineScope.isActive) { _onlineStateFlow.emit(checkOnlineState()) delay(onlineCheckInterval) From 94915937da0e220a08083873dab188e152e4e1c9 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:19:04 +0200 Subject: [PATCH 10/27] Refactor cache system and update tests Removed unnecessary `_id` properties and optimized cache entry updates with `updateWhere` method. Moved test files from `commonTest` to `jvmTest` and ensured all cache-related functionalities are properly tested with adjusted imports. --- .../commonMain/kotlin/cache/CacheEntries.kt | 8 +- .../api/v3/http/PackageSearchApiClient.kt | 100 +++++++++--------- .../kotlin/CacheTests.kt | 7 +- .../{commonTest => jvmTest}/kotlin/Utils.kt | 2 + .../resources/known-repositories.json | 0 .../resources/package-info-by-ids-ktor.json | 0 6 files changed, 59 insertions(+), 58 deletions(-) rename http/client/src/{commonTest => jvmTest}/kotlin/CacheTests.kt (97%) rename http/client/src/{commonTest => jvmTest}/kotlin/Utils.kt (96%) rename http/client/src/{commonTest => jvmTest}/resources/known-repositories.json (100%) rename http/client/src/{commonTest => jvmTest}/resources/package-info-by-ids-ktor.json (100%) diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index a751a3c..4d5d9f6 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -16,7 +16,6 @@ public val SHORT_EXPIRATION_TIME: Duration = 6.hours @Serializable internal data class ApiRepositoryCacheEntry( - val _id: Long? = null, val values: List, val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) ) { @@ -24,10 +23,10 @@ internal data class ApiRepositoryCacheEntry( get() = expires < Clock.System.now() } +internal fun List.toCacheEntry(): ApiRepositoryCacheEntry = ApiRepositoryCacheEntry(this) @Serializable internal data class ApiPackageCacheEntry( - val _id: Long? = null, val apiPackage: ApiPackage, val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) ) { @@ -38,9 +37,10 @@ internal data class ApiPackageCacheEntry( get() = expires < Clock.System.now() } +internal fun ApiPackage.toCacheEntry(): ApiPackageCacheEntry = ApiPackageCacheEntry(this) + @Serializable internal data class SearchPackageRequestCacheEntry( - val _id: Long? = null, val request: SearchPackagesRequest, val packages: List, val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) @@ -53,7 +53,6 @@ internal data class SearchPackageRequestCacheEntry( @Serializable internal data class SearchPackageScrollCacheEntry( - val _id: Long? = null, val scrollId: String, val packages: List, val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) @@ -64,7 +63,6 @@ internal data class SearchPackageScrollCacheEntry( @Serializable internal data class ApiProjectsCacheEntry( - val _id: Long? = null, val queryString: String, val values: List, val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 3f2d5fe..7e9a94c 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -1,11 +1,10 @@ package org.jetbrains.packagesearch.api.v3.http -import cache.ApiPackageCacheEntry import cache.ApiProjectsCacheEntry -import cache.ApiRepositoryCacheEntry import cache.CacheDB import cache.SearchPackageRequestCacheEntry import cache.SearchPackageScrollCacheEntry +import cache.toCacheEntry import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.* @@ -26,6 +25,7 @@ import kotlinx.coroutines.flow.* import kotlinx.document.database.DataStore import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.protobuf.ProtoBuf import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiProject @@ -166,7 +166,8 @@ public class PackageSearchApiClient( requestBuilder?.invoke(this) }.body>() - apiRepositoryCache.insert(ApiRepositoryCacheEntry(cachedResult?._id, results)) + apiRepositoryCache.clear() + apiRepositoryCache.insert(results.toCacheEntry()) return results } @@ -177,7 +178,8 @@ public class PackageSearchApiClient( ): Map = fetchPackageInfo( ids = ids, - requestBuilder = requestBuilder) + requestBuilder = requestBuilder + ) public suspend fun getPackageInfoByIdHashes( ids: Set, @@ -208,7 +210,7 @@ public class PackageSearchApiClient( .map { id -> // NOTE: id depends on the lookupField async { apiPackageCacheDB - .find(lookupField, id) + .find(lookupField, JsonPrimitive(id)) .firstOrNull() ?.let { id to it } // Pair the ID with the cache entry for easier processing } @@ -232,17 +234,17 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ).associateBy { if (lookupField == "id") it.id else it.idHash } - onlineResults.values.forEach { - val cacheKey = if (lookupField == "id") it.id else it.idHash + val fieldPrimitive = JsonPrimitive(if (idHash) it.idHash else it.id) - apiPackageCacheDB.insert( - ApiPackageCacheEntry( - _id = cachedResults[cacheKey]?._id, - it - ) + apiPackageCacheDB.updateWhere( + fieldSelector = lookupField, + fieldValue = fieldPrimitive, + upsert = true, + update = apiPackageCacheDB.insert(it.toCacheEntry()) ) + } // Combine Results @@ -253,16 +255,16 @@ public class PackageSearchApiClient( request: SearchPackagesRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): List { + val isOffline = !onlineStateFlow.value val searchCache = cacheDB.searchPackageCache() - val isOffline = !onlineStateFlow.value - val searchResult = searchCache.find("searchQuery", request.searchQuery) - .firstOrNull() + val searchResult = + searchCache.find("searchQuery", JsonPrimitive(request.searchQuery)) + .firstOrNull() { it.request.packagesType.toSet().containsAll(request.packagesType.toSet()) } searchResult ?.takeIf { isOffline || !it.isExpired } - ?.takeIf { it.request.packagesType.toSet().containsAll(request.packagesType.toSet()) } - ?.let { return it.packages } + ?.also { return it.packages } // cache result not found or expired or not exhaustive enough @@ -271,8 +273,14 @@ public class PackageSearchApiClient( url = endpoints.searchPackages, body = request, requestBuilder = requestBuilder, - ).also { - searchCache.insert(SearchPackageRequestCacheEntry(searchResult?._id, request, it)) + ).also { newPackages -> + + searchCache.updateWhere( + fieldSelector = "searchQuery", + fieldValue = JsonPrimitive(request.searchQuery), + upsert = true, + update = SearchPackageRequestCacheEntry(request, newPackages) + ) } } @@ -283,8 +291,8 @@ public class PackageSearchApiClient( ): SearchPackagesScrollResponse { val cache = cacheDB.scrollStartPackageCache() - val cacheResult = cache - .find("searchQuery", request.searchQuery) + cache + .find("searchQuery", JsonPrimitive(request.searchQuery)) .firstOrNull() ?.also { if (!it.isExpired) return SearchPackagesScrollResponse(it.scrollId, it.packages) @@ -296,9 +304,11 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { - cache.insert( - SearchPackageScrollCacheEntry( - _id = cacheResult?._id, + cache.updateWhere( + fieldSelector = "searchQuery", + fieldValue = JsonPrimitive(request.searchQuery), + upsert = true, + update = SearchPackageScrollCacheEntry( scrollId = it.scrollId, packages = it.data ) @@ -323,7 +333,7 @@ public class PackageSearchApiClient( ): List { val apiProjectsCache = cacheDB.apiProjectsCache() - val cacheResult = apiProjectsCache.find("queryString", request.query) + apiProjectsCache.find("queryString", JsonPrimitive(request.query)) .firstOrNull() ?.also { if (!it.isExpired) return it.values } @@ -333,9 +343,11 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { - apiProjectsCache.insert( - ApiProjectsCacheEntry( - _id = cacheResult?._id, + apiProjectsCache.updateWhere( + fieldSelector = "queryString", + fieldValue = JsonPrimitive(request.query), + upsert = true, + update = ApiProjectsCacheEntry( queryString = request.query, values = it ) @@ -357,30 +369,15 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ) - // update ApiPackageCache - coroutineScope.launch { - val apiPackageCacheDB = cacheDB.apiPackagesCache() - val updates = - results - .map { result -> - async { - apiPackageCacheDB - .find("id", result.id) - .firstOrNull() - ?.let { it._id to result } // Pair the ID with the cache entry for easier processing - } - } - .awaitAll() - .filterNotNull() - .toMap() - updates.forEach { - apiPackageCacheDB.insert(ApiPackageCacheEntry(it.key, it.value)) - } - val updatedIDS = updates.map { it.value.id } - results.filter { it.id !in updatedIDS }.forEach { - apiPackageCacheDB.insert(ApiPackageCacheEntry(apiPackage = it)) - } + results.forEach { + cacheDB.apiPackagesCache().updateWhere( + fieldSelector = "idHash", + fieldValue = JsonPrimitive(it.idHash), + upsert = true, + update = it.toCacheEntry() + ) } + return results } @@ -410,4 +407,3 @@ public class PackageSearchApiClient( } - diff --git a/http/client/src/commonTest/kotlin/CacheTests.kt b/http/client/src/jvmTest/kotlin/CacheTests.kt similarity index 97% rename from http/client/src/commonTest/kotlin/CacheTests.kt rename to http/client/src/jvmTest/kotlin/CacheTests.kt index 624a943..98a87eb 100644 --- a/http/client/src/commonTest/kotlin/CacheTests.kt +++ b/http/client/src/jvmTest/kotlin/CacheTests.kt @@ -12,6 +12,11 @@ import org.jetbrains.packagesearch.api.v3.http.SearchPackagesRequest import org.jetbrains.packagesearch.api.v3.search.PackagesType import org.junit.jupiter.api.Test import kotlin.collections.first +import kotlin.collections.firstOrNull +import kotlin.collections.forEach +import kotlin.collections.map +import kotlin.collections.plus +import kotlin.collections.toSet import kotlin.test.assertEquals import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -120,7 +125,7 @@ class CacheTests { @Test fun `getKnownRepositories test Cache Hit`() = runTest(timeout = 10.seconds) { - val (apiClient, mockEngine ) = setupTestEnv>("known-repositories.json") + val (apiClient, mockEngine) = setupTestEnv>("known-repositories.json") val response1 = apiClient.getKnownRepositories() // Second request (should hit cache) diff --git a/http/client/src/commonTest/kotlin/Utils.kt b/http/client/src/jvmTest/kotlin/Utils.kt similarity index 96% rename from http/client/src/commonTest/kotlin/Utils.kt rename to http/client/src/jvmTest/kotlin/Utils.kt index 168c9b0..abeeffd 100644 --- a/http/client/src/commonTest/kotlin/Utils.kt +++ b/http/client/src/jvmTest/kotlin/Utils.kt @@ -2,6 +2,7 @@ import CacheTests.TestEnv import cache.CacheDB import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.MockEngine.Companion.invoke import io.ktor.client.engine.mock.respond import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.logging.DEFAULT @@ -19,6 +20,7 @@ import kotlinx.serialization.json.Json import org.h2.mvstore.MVStore import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints +import kotlin.collections.filter fun MockEngine.geRequestsFor(url: Url) = diff --git a/http/client/src/commonTest/resources/known-repositories.json b/http/client/src/jvmTest/resources/known-repositories.json similarity index 100% rename from http/client/src/commonTest/resources/known-repositories.json rename to http/client/src/jvmTest/resources/known-repositories.json diff --git a/http/client/src/commonTest/resources/package-info-by-ids-ktor.json b/http/client/src/jvmTest/resources/package-info-by-ids-ktor.json similarity index 100% rename from http/client/src/commonTest/resources/package-info-by-ids-ktor.json rename to http/client/src/jvmTest/resources/package-info-by-ids-ktor.json From d1cb976854dfbee1bdf81ef0de66ba258ac6117c Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:33:32 +0200 Subject: [PATCH 11/27] Refactor cache system and update tests Removed unnecessary `_id` properties and optimized cache entry updates with `updateWhere` method. Moved test files from `commonTest` to `jvmTest` and ensured all cache-related functionalities are properly tested with adjusted imports. --- .github/workflows/test.yml | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4ec59a4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +name: Test with Cache + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: '17' + + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/gradle.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Gradle dependencies + uses: actions/cache@v2 + with: + path: | + ~/.gradle + key: ${{ runner.os }}-gradle-dependencies-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/gradle.properties') }} + restore-keys: | + ${{ runner.os }}-gradle-dependencies- + + - name: Build with Gradle + run: ./gradlew build --no-daemon + + - name: Run tests + run: ./gradlew :http:client:jvmTest --tests "CacheTests" \ No newline at end of file From a66f579e033cbdbd247a617c0ad376b20da28660 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:37:49 +0200 Subject: [PATCH 12/27] Remove Gradle cache steps from GitHub Actions The steps for caching Gradle packages and dependencies have been removed from the test workflow. This change simplifies the workflow and may reduce potential issues related to cache handling. The build step remains unchanged to ensure continuous integration with Gradle. --- .github/workflows/test.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ec59a4..26e86c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,25 +22,6 @@ jobs: distribution: 'adopt' java-version: '17' - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/gradle.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Cache Gradle dependencies - uses: actions/cache@v2 - with: - path: | - ~/.gradle - key: ${{ runner.os }}-gradle-dependencies-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/gradle.properties') }} - restore-keys: | - ${{ runner.os }}-gradle-dependencies- - - name: Build with Gradle run: ./gradlew build --no-daemon From 0755ddd3e3affb3c5a6a1d49f820181205994658 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:41:56 +0200 Subject: [PATCH 13/27] Update workflow to include cache-integration branch Modified the GitHub Actions workflow to trigger on pushes to the cache-integration branch. Removed unnecessary blank lines in the script to --- .github/workflows/test.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 26e86c6..ddc99bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,7 +3,7 @@ name: Test with Cache on: push: branches: - - main + [main, fabrizio.scarponi/cache-integration] pull_request: branches: - main @@ -11,19 +11,15 @@ on: jobs: build: runs-on: ubuntu-latest - steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: 'adopt' java-version: '17' - - name: Build with Gradle run: ./gradlew build --no-daemon - - name: Run tests run: ./gradlew :http:client:jvmTest --tests "CacheTests" \ No newline at end of file From ee4fb920862c0506490ba566259dcf3583b92378 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:47:51 +0200 Subject: [PATCH 14/27] Remove unused DateTimeFormatter class and update CI config Deleted the DateTimeFormatter implementation in the Apple main source set as it was no longer needed. Updated the CI configuration to include a Gradle Enterprise key for the build process. --- .github/workflows/test.yml | 2 ++ .../normalization/DateTimeFormatter.kt | 25 ------------------- 2 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 version-utils/src/appleMain/kotlin/org/jetbrains/packagesearch/packageversionutils/normalization/DateTimeFormatter.kt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ddc99bf..5cf841e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,6 +20,8 @@ jobs: distribution: 'adopt' java-version: '17' - name: Build with Gradle + env: + GRADLE_ENTERPRISE_KEY: ${{ secrets.GRADLE_ENTERPRISE_KEY }} run: ./gradlew build --no-daemon - name: Run tests run: ./gradlew :http:client:jvmTest --tests "CacheTests" \ No newline at end of file diff --git a/version-utils/src/appleMain/kotlin/org/jetbrains/packagesearch/packageversionutils/normalization/DateTimeFormatter.kt b/version-utils/src/appleMain/kotlin/org/jetbrains/packagesearch/packageversionutils/normalization/DateTimeFormatter.kt deleted file mode 100644 index 5e3bfa7..0000000 --- a/version-utils/src/appleMain/kotlin/org/jetbrains/packagesearch/packageversionutils/normalization/DateTimeFormatter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.jetbrains.packagesearch.packageversionutils.normalization - -import kotlinx.datetime.Instant -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime -import platform.Foundation.NSDateFormatter -import platform.Foundation.timeIntervalSince1970 - -public fun NSDateFormatter(pattern: String): NSDateFormatter = NSDateFormatter().apply { dateFormat = pattern } - -public actual fun DateTimeFormatter(pattern: String): DateTimeFormatter = DateTimeFormatter(NSDateFormatter(pattern)) - -public actual class DateTimeFormatter internal constructor(private val delegate: NSDateFormatter) { - public actual fun parseOrNull(dateTimeString: String): LocalDateTime? = - delegate.dateFromString(dateTimeString) - ?.timeIntervalSince1970 - ?.toLong() - ?.let { Instant.fromEpochSeconds(it, 0) } - ?.toLocalDateTime(TimeZone.currentSystemDefault()) - - public actual fun parse(dateTimeString: String): LocalDateTime = - parseOrNull(dateTimeString) - ?: error("Unable to parse '$dateTimeString' with pattern '${delegate.dateFormat}'") -} From 4aead4aa3d7ffeedd2ded2d83df3ce6cdf8b173d Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:51:27 +0200 Subject: [PATCH 15/27] Remove appleMain dependencies and update CI workflow Removed the appleMain block in build-systems/build.gradle.kts to streamline the codebase. Modified the GitHub Actions workflow to focus on running specific tests for the cache system and --- .github/workflows/test.yml | 12 ++++++------ build-systems/build.gradle.kts | 5 ----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5cf841e..b8abf6b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ on: - main jobs: - build: + build-and-test: runs-on: ubuntu-latest steps: - name: Checkout repository @@ -19,9 +19,9 @@ jobs: with: distribution: 'adopt' java-version: '17' - - name: Build with Gradle - env: - GRADLE_ENTERPRISE_KEY: ${{ secrets.GRADLE_ENTERPRISE_KEY }} - run: ./gradlew build --no-daemon - - name: Run tests +# - name: Build with Gradle +# env: +# GRADLE_ENTERPRISE_KEY: ${{ secrets.GRADLE_ENTERPRISE_KEY }} +# run: ./gradlew build --no-daemon + - name: Run tests for cache system run: ./gradlew :http:client:jvmTest --tests "CacheTests" \ No newline at end of file diff --git a/build-systems/build.gradle.kts b/build-systems/build.gradle.kts index a3b1a82..bea74d8 100644 --- a/build-systems/build.gradle.kts +++ b/build-systems/build.gradle.kts @@ -36,11 +36,6 @@ kotlin { api(packageSearchApiModelsVersions.ktor.client.js) } } - appleMain { - dependencies { - api(packageSearchApiModelsVersions.ktor.client.darwin) - } - } jvmTest { dependencies { implementation(packageSearchApiModelsVersions.kotlinx.coroutines.test) From e0570be080fe230416368c82d9ceaa50f9762797 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Tue, 23 Jul 2024 13:54:48 +0200 Subject: [PATCH 16/27] Update workflow branches and clean up test script --- .github/workflows/test.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b8abf6b..f8c7ee2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,10 +3,10 @@ name: Test with Cache on: push: branches: - [main, fabrizio.scarponi/cache-integration] + [main, master] pull_request: branches: - - main + [main, master] jobs: build-and-test: @@ -19,9 +19,5 @@ jobs: with: distribution: 'adopt' java-version: '17' -# - name: Build with Gradle -# env: -# GRADLE_ENTERPRISE_KEY: ${{ secrets.GRADLE_ENTERPRISE_KEY }} -# run: ./gradlew build --no-daemon - name: Run tests for cache system run: ./gradlew :http:client:jvmTest --tests "CacheTests" \ No newline at end of file From 8e470e7f7ddb69a600360654ab4c8d20f2fb221f Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 24 Jul 2024 10:19:15 +0200 Subject: [PATCH 17/27] Update workflow branches and clean up test script --- http/client/src/commonMain/kotlin/cache/CacheEntries.kt | 1 - http/client/src/commonMain/kotlin/cache/Collections.kt | 2 +- .../packagesearch/api/v3/http/PackageSearchApiClient.kt | 4 ++-- settings.gradle.kts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index 4d5d9f6..7420fe7 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -45,7 +45,6 @@ internal data class SearchPackageRequestCacheEntry( val packages: List, val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) ) { - val searchQuery: String = request.searchQuery val isExpired: Boolean get() = expires < Clock.System.now() diff --git a/http/client/src/commonMain/kotlin/cache/Collections.kt b/http/client/src/commonMain/kotlin/cache/Collections.kt index 5a7ed7e..e28b8c3 100644 --- a/http/client/src/commonMain/kotlin/cache/Collections.kt +++ b/http/client/src/commonMain/kotlin/cache/Collections.kt @@ -23,7 +23,7 @@ internal class CacheDB(val dataStore: DataStore) { suspend fun searchPackageCache()= db.getObjectCollection("SEARCH_PACKAGES").apply { getAllIndexNames().ifEmpty { - createIndex(SearchPackageRequestCacheEntry::searchQuery.name) + createIndex(SearchPackageRequestCacheEntry::request.name) } } diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 7e9a94c..89346fd 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -259,7 +259,7 @@ public class PackageSearchApiClient( val searchCache = cacheDB.searchPackageCache() val searchResult = - searchCache.find("searchQuery", JsonPrimitive(request.searchQuery)) + searchCache.find(SearchPackageRequestCacheEntry::request.name, request) .firstOrNull() { it.request.packagesType.toSet().containsAll(request.packagesType.toSet()) } searchResult @@ -276,7 +276,7 @@ public class PackageSearchApiClient( ).also { newPackages -> searchCache.updateWhere( - fieldSelector = "searchQuery", + fieldSelector = "request", fieldValue = JsonPrimitive(request.searchQuery), upsert = true, update = SearchPackageRequestCacheEntry(request, newPackages) diff --git a/settings.gradle.kts b/settings.gradle.kts index 5d3661a..b58e13d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ dependencyResolutionManagement { from(files("packagesearch-api-models.versions.toml")) } create("kotlinxDocumentStore") { - from("com.github.lamba92:kotlinx-document-store-version-catalog:1.0.0-SNAPSHOT") + from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev1") } } } From 7b918236e35cee1eecd72a264dab9936b0fbdae0 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 24 Jul 2024 10:37:34 +0200 Subject: [PATCH 18/27] Refactor cache query methods to use serializers Updated cache query methods in `PackageSearchApiClient` to include explicit serializers. --- .../api/v3/http/PackageSearchApiClient.kt | 13 +++++++++---- settings.gradle.kts | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 89346fd..75720e2 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.document.database.DataStore import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.protobuf.ProtoBuf @@ -210,7 +211,11 @@ public class PackageSearchApiClient( .map { id -> // NOTE: id depends on the lookupField async { apiPackageCacheDB - .find(lookupField, JsonPrimitive(id)) + .find( + lookupField, + id, + String.serializer() + ) .firstOrNull() ?.let { id to it } // Pair the ID with the cache entry for easier processing } @@ -259,7 +264,7 @@ public class PackageSearchApiClient( val searchCache = cacheDB.searchPackageCache() val searchResult = - searchCache.find(SearchPackageRequestCacheEntry::request.name, request) + searchCache.find(SearchPackageRequestCacheEntry::request.name, request, SearchPackagesRequest.serializer()) .firstOrNull() { it.request.packagesType.toSet().containsAll(request.packagesType.toSet()) } searchResult @@ -292,7 +297,7 @@ public class PackageSearchApiClient( val cache = cacheDB.scrollStartPackageCache() cache - .find("searchQuery", JsonPrimitive(request.searchQuery)) + .find("searchQuery", request.searchQuery, String.serializer()) .firstOrNull() ?.also { if (!it.isExpired) return SearchPackagesScrollResponse(it.scrollId, it.packages) @@ -333,7 +338,7 @@ public class PackageSearchApiClient( ): List { val apiProjectsCache = cacheDB.apiProjectsCache() - apiProjectsCache.find("queryString", JsonPrimitive(request.query)) + apiProjectsCache.find("queryString", request.query, String.serializer()) .firstOrNull() ?.also { if (!it.isExpired) return it.values } diff --git a/settings.gradle.kts b/settings.gradle.kts index b58e13d..1b6816b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ dependencyResolutionManagement { from(files("packagesearch-api-models.versions.toml")) } create("kotlinxDocumentStore") { - from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev1") + from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev4") } } } From d6d3c4b0bf248b558fc7adc4d936d0e5c4b0e601 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 24 Jul 2024 12:01:57 +0200 Subject: [PATCH 19/27] Remove redundant ListPackageSearchEndpoint file The ListPackageSearchEndpoint file was deleted as it contained a redundant main function that printed URLs already defined in the PackageSearchEndpoints object. This cleanup simplifies the codebase and removes unnecessary duplication. --- .../api/v3/http/ListPackageSearchEnpoint.kt | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt diff --git a/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt b/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt deleted file mode 100644 index e817ff1..0000000 --- a/http/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/ListPackageSearchEnpoint.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.jetbrains.packagesearch.api.v3.http - -import io.ktor.http.Url - -public fun main() { - - fun printUrl(url: Url) = println(url.toString()) - - PackageSearchEndpoints.PROD.apply { - printUrl(health) - printUrl(nextScroll) - printUrl(knownRepositories) - printUrl(packageInfoByIdHashes) - printUrl(packageInfoByIds) - printUrl(refreshPackagesInfo) - printUrl(searchPackages) - printUrl(searchProjects) - printUrl(startScroll) - } -} -/* -https://api.prod.package-search.services.jetbrains.com/health -https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/next -https://api.prod.package-search.services.jetbrains.com/known-repositories -https://api.prod.package-search.services.jetbrains.com/package-info-by-id-hashes -https://api.prod.package-search.services.jetbrains.com/package-info-by-ids -https://api.prod.package-search.services.jetbrains.com/refresh-packages-info -https://api.prod.package-search.services.jetbrains.com/search-packages -https://api.prod.package-search.services.jetbrains.com/search-projects -https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/start -https://api.prod.package-search.services.jetbrains.com/health -https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/next -https://api.prod.package-search.services.jetbrains.com/known-repositories -https://api.prod.package-search.services.jetbrains.com/package-info-by-id-hashes -https://api.prod.package-search.services.jetbrains.com/package-info-by-ids -https://api.prod.package-search.services.jetbrains.com/refresh-packages-info -https://api.prod.package-search.services.jetbrains.com/search-packages -https://api.prod.package-search.services.jetbrains.com/search-projects -https://api.prod.package-search.services.jetbrains.com/search-packages/scroll/ - - */ \ No newline at end of file From 667908ab07bedd292ece24a2e2f72e09f78f6bb1 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 24 Jul 2024 12:04:29 +0200 Subject: [PATCH 20/27] Remove redundant ListPackageSearchEndpoint file The ListPackageSearchEndpoint file was deleted as it contained a redundant main function that printed URLs already defined in the PackageSearchEndpoints object. This cleanup simplifies the codebase and removes unnecessary duplication. --- .../packagesearch/api/v3/http/PackageSearchApiClient.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 75720e2..69b3bc5 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -212,9 +212,9 @@ public class PackageSearchApiClient( async { apiPackageCacheDB .find( - lookupField, - id, - String.serializer() + selector = lookupField, + value = id, + valueSerializer = String.serializer() ) .firstOrNull() ?.let { id to it } // Pair the ID with the cache entry for easier processing From 9fde1a180ad0378b8204a49baa48496103ba0732 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 24 Jul 2024 13:41:42 +0200 Subject: [PATCH 21/27] wip --- .../commonMain/kotlin/cache/CacheEntries.kt | 62 +----- .../commonMain/kotlin/cache/Collections.kt | 41 ---- .../api/v3/http/PackageSearchApiClient.kt | 183 ++++++++++-------- settings.gradle.kts | 6 +- 4 files changed, 112 insertions(+), 180 deletions(-) delete mode 100644 http/client/src/commonMain/kotlin/cache/Collections.kt diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index 7420fe7..8da9722 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -3,69 +3,21 @@ package cache import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlinx.serialization.Serializable -import org.jetbrains.packagesearch.api.v3.ApiPackage -import org.jetbrains.packagesearch.api.v3.ApiProject -import org.jetbrains.packagesearch.api.v3.ApiRepository -import org.jetbrains.packagesearch.api.v3.http.SearchPackagesRequest import kotlin.time.Duration import kotlin.time.Duration.Companion.hours -public val DEFAULT_EXPIRATION_TIME: Duration = 12.hours -public val SHORT_EXPIRATION_TIME: Duration = 6.hours +internal val DEFAULT_EXPIRATION_TIME: Duration = 12.hours @Serializable -internal data class ApiRepositoryCacheEntry( - val values: List, - val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) +internal data class CacheEntry( + val key: K, + val value: V, + val createdAt: Instant = Clock.System.now() ) { - val isExpired: Boolean - get() = expires < Clock.System.now() -} -internal fun List.toCacheEntry(): ApiRepositoryCacheEntry = ApiRepositoryCacheEntry(this) + internal fun isExpired(expirationTime: Duration = DEFAULT_EXPIRATION_TIME): Boolean = + createdAt + expirationTime > Clock.System.now() -@Serializable -internal data class ApiPackageCacheEntry( - val apiPackage: ApiPackage, - val expires: Instant = Clock.System.now().plus(DEFAULT_EXPIRATION_TIME) -) { - val id: String = apiPackage.id - - val idHash: String = apiPackage.idHash - val isExpired: Boolean - get() = expires < Clock.System.now() -} - -internal fun ApiPackage.toCacheEntry(): ApiPackageCacheEntry = ApiPackageCacheEntry(this) - -@Serializable -internal data class SearchPackageRequestCacheEntry( - val request: SearchPackagesRequest, - val packages: List, - val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) -) { - val isExpired: Boolean - get() = expires < Clock.System.now() -} - -@Serializable -internal data class SearchPackageScrollCacheEntry( - val scrollId: String, - val packages: List, - val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) -) { - val isExpired: Boolean - get() = expires < Clock.System.now() -} - -@Serializable -internal data class ApiProjectsCacheEntry( - val queryString: String, - val values: List, - val expires: Instant = Clock.System.now().plus(SHORT_EXPIRATION_TIME) -) { - val isExpired: Boolean - get() = expires < Clock.System.now() } \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/cache/Collections.kt b/http/client/src/commonMain/kotlin/cache/Collections.kt deleted file mode 100644 index e28b8c3..0000000 --- a/http/client/src/commonMain/kotlin/cache/Collections.kt +++ /dev/null @@ -1,41 +0,0 @@ -package cache - -import kotlinx.document.database.DataStore -import kotlinx.document.database.KotlinxDocumentDatabase -import kotlinx.document.database.getObjectCollection - -internal class CacheDB(val dataStore: DataStore) { - val db = KotlinxDocumentDatabase { - store = dataStore - } - - suspend fun apiRepositoryCache() = - db.getObjectCollection("API_REPOSITORIES") - - suspend fun apiPackagesCache() = - db.getObjectCollection("PACKAGES").apply { - getAllIndexNames().ifEmpty { - createIndex(ApiPackageCacheEntry::id.name) - createIndex(ApiPackageCacheEntry::idHash.name) - } - } - - suspend fun searchPackageCache()= - db.getObjectCollection("SEARCH_PACKAGES").apply { - getAllIndexNames().ifEmpty { - createIndex(SearchPackageRequestCacheEntry::request.name) - } - } - - suspend fun scrollStartPackageCache() = - db.getObjectCollection("SCROLL_PACKAGES") - - suspend fun apiProjectsCache() = - db.getObjectCollection("API_PROJECTS").apply { - getAllIndexNames().ifEmpty { - createIndex(ApiProjectsCacheEntry::queryString.name) - } - } - - -} \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 69b3bc5..ff97d84 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -2,6 +2,8 @@ package org.jetbrains.packagesearch.api.v3.http import cache.ApiProjectsCacheEntry import cache.CacheDB +import cache.CacheEntry +import cache.DEFAULT_EXPIRATION_TIME import cache.SearchPackageRequestCacheEntry import cache.SearchPackageScrollCacheEntry import cache.toCacheEntry @@ -23,10 +25,17 @@ import io.ktor.util.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.document.database.DataStore +import kotlinx.document.database.KotlinxDocumentDatabase +import kotlinx.document.database.find +import kotlinx.document.database.getObjectCollection +import kotlinx.document.database.updateWhere import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.protobuf.ProtoBuf import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiProject @@ -36,24 +45,50 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds +internal typealias SearchCacheEntry = CacheEntry> +internal typealias PackageCacheEntry = CacheEntry +internal typealias MiscellaneousCacheEntry = CacheEntry + public class PackageSearchApiClient( dataStore: DataStore, private val httpClient: HttpClient = defaultHttpClient(), public val endpoints: PackageSearchEndpoints, - private val coroutineScope: CoroutineScope = CoroutineScope(httpClient.coroutineContext), - onlineCheckInterval: Duration = 1.minutes + coroutineScope: CoroutineScope = CoroutineScope(httpClient.coroutineContext), + onlineCheckInterval: Duration = 1.minutes, + public val cacheDuration: Duration = DEFAULT_EXPIRATION_TIME, ) { - private val cacheDB = CacheDB(dataStore) + private val kotlinxDb = KotlinxDocumentDatabase(dataStore) + + private val searchCacheCollection = coroutineScope.async { + kotlinxDb.getObjectCollection("SearchRequests") + .apply { createIndex(SearchCacheEntry::key.name) } + } - private val _onlineStateFlow = MutableStateFlow(true) - public val onlineStateFlow: StateFlow = _onlineStateFlow + private val repositoriesCacheCollection = coroutineScope.async { + kotlinxDb.getObjectCollection("MiscellaneousCacheEntry") + .apply { createIndex(MiscellaneousCacheEntry::key.name) } + } - init { - initiateOnlineCheckJob(onlineCheckInterval) + private val packagesCacheCollection = coroutineScope.async { + kotlinxDb.getObjectCollection("PackageCacheEntry") + .apply { createIndex(PackageCacheEntry::key.name) } } + public val onlineStateFlow: StateFlow = + flow { + while (true) { + val result = httpClient.get(endpoints.health) { + header(HttpHeaders.Accept, ContentType.Text.Plain) + } + emit(result.status.isSuccess()) + delay(onlineCheckInterval) + } + } + .retry() + .stateIn(coroutineScope, SharingStarted.Lazily, true) + @Serializable private data class Error(val error: Inner) { @Serializable @@ -155,11 +190,19 @@ public class PackageSearchApiClient( ) = defaultRawRequest(method, url, body, requestBuilder, cache).body() public suspend fun getKnownRepositories(requestBuilder: (HttpRequestBuilder.() -> Unit)? = null): List { - val apiRepositoryCache = cacheDB.apiRepositoryCache() + val apiRepositoryCache = repositoriesCacheCollection.await() val isOffline = !onlineStateFlow.value - val cachedResult = apiRepositoryCache.iterateAll().firstOrNull() + val cachedResult = apiRepositoryCache.find( + selector = "key", + value = "repositories" + ).singleOrNull() + + cachedResult?.let { + if (isOffline || !it.isExpired()) { + return apiRepositoryCache.json.decodeFromJsonElement>(it.value) + } - cachedResult?.let { if (isOffline || !it.isExpired) return it.values } + } val results = httpClient.request(endpoints.knownRepositories) { method = HttpMethod.Get @@ -167,93 +210,95 @@ public class PackageSearchApiClient( requestBuilder?.invoke(this) }.body>() - apiRepositoryCache.clear() - apiRepositoryCache.insert(results.toCacheEntry()) + apiRepositoryCache.insert( + MiscellaneousCacheEntry( + key = "repositories", + value = apiRepositoryCache.json.encodeToJsonElement(results) + ) + ) + return results } - public suspend fun getPackageInfoByIds( ids: Set, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = - fetchPackageInfo( - ids = ids, - requestBuilder = requestBuilder + getPackageInfoByIdHashes( + ids.map { ApiPackage.hashPackageId(it) }.toSet(), + requestBuilder ) public suspend fun getPackageInfoByIdHashes( - ids: Set, - requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, - ): Map = - fetchPackageInfo( - ids = ids, - idHash = true, - requestBuilder = requestBuilder - ) - - // Common Function to Fetch Package Info (Handles Both ID and ID Hash Retrieval) - private suspend fun fetchPackageInfo( - ids: Set, - idHash: Boolean = false, // "idHash" or "id" + idHashes: Set, + useHashes: Boolean, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = coroutineScope { - val apiPackageCacheDB = cacheDB.apiPackagesCache() - val isOffline = !onlineStateFlow.value + val packagesCache = packagesCacheCollection.await() - val lookupField = if (idHash) "idHash" else "id" - val endpointUrl = - if (idHash) endpoints.packageInfoByIdHashes else endpoints.packageInfoByIds + val isOffline = !onlineStateFlow.value val cachedResults = - ids + idHashes .map { id -> // NOTE: id depends on the lookupField async { - apiPackageCacheDB - .find( - selector = lookupField, - value = id, - valueSerializer = String.serializer() - ) + packagesCache + .find(PackageCacheEntry::key.name, value = id) .firstOrNull() - ?.let { id to it } // Pair the ID with the cache entry for easier processing } } .awaitAll() .filterNotNull() - .toMap() - val validResults = if (!isOffline) cachedResults.filter { !it.value.isExpired } else cachedResults + val cachedResultMap = cachedResults + .mapNotNull { it.value } + .associateBy { if (useHashes) it.idHash else it.id } + val validResults = + when { + !isOffline -> cachedResults.filter { !it.isExpired(cacheDuration) } + else -> return@coroutineScope cachedResultMap + } - val unresolvedIdentifiers = ids - validResults.keys + val unresolvedIdentifiers = idHashes - validResults.map { it.key } if (unresolvedIdentifiers.isEmpty()) { - return@coroutineScope validResults.mapValues { it.value.apiPackage } + return@coroutineScope validResults + .mapNotNull { it.value } + .associateBy { if (useHashes) it.idHash else it.id } } val onlineResults = defaultRequest<_, List>( method = HttpMethod.Post, - url = endpointUrl, + url = endpoints.packageInfoByIdHashes, body = GetPackageInfoRequest(unresolvedIdentifiers), requestBuilder = requestBuilder, - ).associateBy { if (lookupField == "id") it.id else it.idHash } - - onlineResults.values.forEach { + ).associateBy { it.idHash } - val fieldPrimitive = JsonPrimitive(if (idHash) it.idHash else it.id) + val notFoundPackages = idHashes - cachedResults.map { it.key } - onlineResults.keys - apiPackageCacheDB.updateWhere( - fieldSelector = lookupField, - fieldValue = fieldPrimitive, + notFoundPackages.forEach { + packagesCache.updateWhere( + fieldSelector = PackageCacheEntry::key.name, + fieldValue = it, upsert = true, - update = apiPackageCacheDB.insert(it.toCacheEntry()) + update = PackageCacheEntry(it, null) ) + } + onlineResults.values.forEach { + packagesCache.updateWhere( + fieldSelector = PackageCacheEntry::key.name, + fieldValue = it.idHash, + upsert = true, + update = PackageCacheEntry(it.idHash, it) + ) } - // Combine Results - onlineResults + cachedResults.mapValues { it.value.apiPackage } + val onlineResultsMappedKeys = onlineResults.mapKeys { if (useHashes) it.key else it.value.id } + + // Map Combine Results + onlineResultsMappedKeys + cachedResultMap } public suspend fun searchPackages( @@ -279,6 +324,7 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { newPackages -> + //todo update apipackages cache now! searchCache.updateWhere( fieldSelector = "request", @@ -387,28 +433,5 @@ public class PackageSearchApiClient( } - private suspend fun checkOnlineState(): Boolean { - val request = - kotlin.runCatching { - httpClient.get(endpoints.health) { - header(HttpHeaders.Accept, ContentType.Text.Plain) - } - } - val isOnline = - request - .map { it.status.isSuccess() } - .getOrDefault(false) - return isOnline - } - - private fun initiateOnlineCheckJob(onlineCheckInterval: Duration) { - coroutineScope.launch { - while (coroutineScope.isActive) { - _onlineStateFlow.emit(checkOnlineState()) - delay(onlineCheckInterval) - } - } - } - } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1b6816b..b2d6a09 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,16 +12,14 @@ dependencyResolutionManagement { repositories { mavenCentral() maven("https://packages.jetbrains.team/maven/p/kpm/public") - maven { - url = uri("https://plugins.gradle.org/m2/") - } + gradlePluginPortal() } versionCatalogs { create("packageSearchApiModelsVersions") { from(files("packagesearch-api-models.versions.toml")) } create("kotlinxDocumentStore") { - from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev4") + from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev5") } } } From e57e5b389875f422eabf67eeebb5a2a0769e6949 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Wed, 24 Jul 2024 17:28:51 +0200 Subject: [PATCH 22/27] Refactor cache handling to improve efficiency Replaced manual cache updates with generic insert operations and removed unused imports. The changes make the code easier to maintain and extend by leveraging a more consistent cache handling approach. This should also help in reducing the potential for bugs related to cache synchronization. --- .../api/v3/http/PackageSearchApiClient.kt | 101 ++++++++---------- 1 file changed, 42 insertions(+), 59 deletions(-) diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index ff97d84..88b4c1c 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -1,12 +1,7 @@ package org.jetbrains.packagesearch.api.v3.http -import cache.ApiProjectsCacheEntry -import cache.CacheDB import cache.CacheEntry import cache.DEFAULT_EXPIRATION_TIME -import cache.SearchPackageRequestCacheEntry -import cache.SearchPackageScrollCacheEntry -import cache.toCacheEntry import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.* @@ -30,10 +25,8 @@ import kotlinx.document.database.find import kotlinx.document.database.getObjectCollection import kotlinx.document.database.updateWhere import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.protobuf.ProtoBuf @@ -47,6 +40,8 @@ import kotlin.time.Duration.Companion.seconds internal typealias SearchCacheEntry = CacheEntry> internal typealias PackageCacheEntry = CacheEntry +internal typealias ScrollRequestCacheEntry = CacheEntry +internal typealias ApiProjectCacheEntry = CacheEntry> internal typealias MiscellaneousCacheEntry = CacheEntry public class PackageSearchApiClient( @@ -66,15 +61,25 @@ public class PackageSearchApiClient( } private val repositoriesCacheCollection = coroutineScope.async { - kotlinxDb.getObjectCollection("MiscellaneousCacheEntry") + kotlinxDb.getObjectCollection("Miscellaneous") .apply { createIndex(MiscellaneousCacheEntry::key.name) } } private val packagesCacheCollection = coroutineScope.async { - kotlinxDb.getObjectCollection("PackageCacheEntry") + kotlinxDb.getObjectCollection("Packages") .apply { createIndex(PackageCacheEntry::key.name) } } + private val scrollRequestCacheCollection = coroutineScope.async { + kotlinxDb.getObjectCollection("StartScrollRequests") + .apply { createIndex(ScrollRequestCacheEntry::key.name) } + } + + private val apiProjectCacheCollection = coroutineScope.async { + kotlinxDb.getObjectCollection("ApiProjects") + .apply { createIndex(ApiProjectCacheEntry::key.name) } + } + public val onlineStateFlow: StateFlow = flow { @@ -225,8 +230,9 @@ public class PackageSearchApiClient( requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = getPackageInfoByIdHashes( - ids.map { ApiPackage.hashPackageId(it) }.toSet(), - requestBuilder + idHashes = ids.map { ApiPackage.hashPackageId(it) }.toSet(), + useHashes = false, + requestBuilder = requestBuilder ) public suspend fun getPackageInfoByIdHashes( @@ -306,15 +312,13 @@ public class PackageSearchApiClient( requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): List { val isOffline = !onlineStateFlow.value - val searchCache = cacheDB.searchPackageCache() - val searchResult = - searchCache.find(SearchPackageRequestCacheEntry::request.name, request, SearchPackagesRequest.serializer()) - .firstOrNull() { it.request.packagesType.toSet().containsAll(request.packagesType.toSet()) } + val searchCache = searchCacheCollection.await() - searchResult - ?.takeIf { isOffline || !it.isExpired } - ?.also { return it.packages } + searchCache.find(selector = SearchCacheEntry::key.name, value = request) + .firstOrNull() + ?.takeIf { isOffline || !it.isExpired() } + ?.also { return it.value } // cache result not found or expired or not exhaustive enough @@ -324,14 +328,11 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { newPackages -> - //todo update apipackages cache now! - - searchCache.updateWhere( - fieldSelector = "request", - fieldValue = JsonPrimitive(request.searchQuery), - upsert = true, - update = SearchPackageRequestCacheEntry(request, newPackages) - ) + searchCache.insert(SearchCacheEntry(request, newPackages)) + val packagesCache = packagesCacheCollection.await() + newPackages.forEach { + packagesCache.insert(PackageCacheEntry(it.idHash, it)) + } } } @@ -340,13 +341,12 @@ public class PackageSearchApiClient( request: SearchPackagesStartScrollRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): SearchPackagesScrollResponse { - val cache = cacheDB.scrollStartPackageCache() - cache - .find("searchQuery", request.searchQuery, String.serializer()) + val scrollCache = scrollRequestCacheCollection.await() + scrollCache.find(selector = ScrollRequestCacheEntry::key.name, value = request) .firstOrNull() ?.also { - if (!it.isExpired) return SearchPackagesScrollResponse(it.scrollId, it.packages) + if (!it.isExpired()) return it.value } return defaultRequest<_, SearchPackagesScrollResponse>( @@ -355,15 +355,11 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { - cache.updateWhere( - fieldSelector = "searchQuery", - fieldValue = JsonPrimitive(request.searchQuery), - upsert = true, - update = SearchPackageScrollCacheEntry( - scrollId = it.scrollId, - packages = it.data - ) - ) + scrollCache.insert(ScrollRequestCacheEntry(request, it)) + val packagesCache = packagesCacheCollection.await() + it.data.forEach { + packagesCache.insert(PackageCacheEntry(it.idHash, it)) + } } } @@ -382,11 +378,11 @@ public class PackageSearchApiClient( request: SearchProjectRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)?, ): List { - val apiProjectsCache = cacheDB.apiProjectsCache() - apiProjectsCache.find("queryString", request.query, String.serializer()) - .firstOrNull() - ?.also { if (!it.isExpired) return it.values } + val apiProjectsCache = apiProjectCacheCollection.await() + + apiProjectsCache.find(selector = SearchCacheEntry::key.name, value = request).firstOrNull() + ?.also { if (!it.isExpired()) return it.value } return defaultRequest<_, List>( method = HttpMethod.Post, @@ -394,15 +390,7 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { - apiProjectsCache.updateWhere( - fieldSelector = "queryString", - fieldValue = JsonPrimitive(request.query), - upsert = true, - update = ApiProjectsCacheEntry( - queryString = request.query, - values = it - ) - ) + apiProjectsCache.insert(ApiProjectCacheEntry(request, it)) } } @@ -420,15 +408,10 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ) + val packagesCache = packagesCacheCollection.await() results.forEach { - cacheDB.apiPackagesCache().updateWhere( - fieldSelector = "idHash", - fieldValue = JsonPrimitive(it.idHash), - upsert = true, - update = it.toCacheEntry() - ) + packagesCache.insert(PackageCacheEntry(it.idHash, it)) } - return results } From 17960446c7f0ac962a18ff3bb717b7845fa42740 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Thu, 25 Jul 2024 13:07:00 +0200 Subject: [PATCH 23/27] Refactor cache handling to improve efficiency - Replaced manual cache updates with generic insert operations - Removed unused imports - Improved cache synchronization and maintainability --- .../commonMain/kotlin/cache/CacheEntries.kt | 2 +- .../api/v3/http/PackageSearchApiClient.kt | 78 +++++++++++++------ http/client/src/jvmTest/kotlin/CacheTests.kt | 56 +++++++------ http/client/src/jvmTest/kotlin/Utils.kt | 4 - settings.gradle.kts | 2 +- 5 files changed, 91 insertions(+), 51 deletions(-) diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index 8da9722..2416237 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -17,7 +17,7 @@ internal data class CacheEntry( ) { internal fun isExpired(expirationTime: Duration = DEFAULT_EXPIRATION_TIME): Boolean = - createdAt + expirationTime > Clock.System.now() + createdAt + expirationTime < Clock.System.now() } \ No newline at end of file diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 88b4c1c..380cf93 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -19,11 +19,7 @@ import io.ktor.serialization.kotlinx.protobuf.* import io.ktor.util.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import kotlinx.document.database.DataStore -import kotlinx.document.database.KotlinxDocumentDatabase -import kotlinx.document.database.find -import kotlinx.document.database.getObjectCollection -import kotlinx.document.database.updateWhere +import kotlinx.document.database.* import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement @@ -55,27 +51,27 @@ public class PackageSearchApiClient( private val kotlinxDb = KotlinxDocumentDatabase(dataStore) - private val searchCacheCollection = coroutineScope.async { + internal val searchCacheCollection = coroutineScope.async { kotlinxDb.getObjectCollection("SearchRequests") .apply { createIndex(SearchCacheEntry::key.name) } } - private val repositoriesCacheCollection = coroutineScope.async { + internal val repositoriesCacheCollection = coroutineScope.async { kotlinxDb.getObjectCollection("Miscellaneous") .apply { createIndex(MiscellaneousCacheEntry::key.name) } } - private val packagesCacheCollection = coroutineScope.async { + internal val packagesCacheCollection = coroutineScope.async { kotlinxDb.getObjectCollection("Packages") .apply { createIndex(PackageCacheEntry::key.name) } } - private val scrollRequestCacheCollection = coroutineScope.async { + internal val scrollRequestCacheCollection = coroutineScope.async { kotlinxDb.getObjectCollection("StartScrollRequests") .apply { createIndex(ScrollRequestCacheEntry::key.name) } } - private val apiProjectCacheCollection = coroutineScope.async { + internal val apiProjectCacheCollection = coroutineScope.async { kotlinxDb.getObjectCollection("ApiProjects") .apply { createIndex(ApiProjectCacheEntry::key.name) } } @@ -198,7 +194,7 @@ public class PackageSearchApiClient( val apiRepositoryCache = repositoriesCacheCollection.await() val isOffline = !onlineStateFlow.value val cachedResult = apiRepositoryCache.find( - selector = "key", + selector = MiscellaneousCacheEntry::key.name, value = "repositories" ).singleOrNull() @@ -215,8 +211,11 @@ public class PackageSearchApiClient( requestBuilder?.invoke(this) }.body>() - apiRepositoryCache.insert( - MiscellaneousCacheEntry( + apiRepositoryCache.updateWhere( + fieldSelector = MiscellaneousCacheEntry::key.name, + fieldValue = "repositories", + upsert = true, + update = MiscellaneousCacheEntry( key = "repositories", value = apiRepositoryCache.json.encodeToJsonElement(results) ) @@ -237,7 +236,7 @@ public class PackageSearchApiClient( public suspend fun getPackageInfoByIdHashes( idHashes: Set, - useHashes: Boolean, + useHashes: Boolean = true, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = coroutineScope { @@ -266,7 +265,10 @@ public class PackageSearchApiClient( else -> return@coroutineScope cachedResultMap } - val unresolvedIdentifiers = idHashes - validResults.map { it.key } + val resultIds = validResults.map { it.key } + + val unresolvedIdentifiers = + idHashes - resultIds if (unresolvedIdentifiers.isEmpty()) { return@coroutineScope validResults @@ -281,7 +283,7 @@ public class PackageSearchApiClient( requestBuilder = requestBuilder, ).associateBy { it.idHash } - val notFoundPackages = idHashes - cachedResults.map { it.key } - onlineResults.keys + val notFoundPackages = idHashes - cachedResults.map { it.key }.toSet() - onlineResults.keys notFoundPackages.forEach { packagesCache.updateWhere( @@ -328,10 +330,20 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { newPackages -> - searchCache.insert(SearchCacheEntry(request, newPackages)) + searchCache.updateWhere( + fieldSelector = SearchCacheEntry::key.name, + fieldValue = request, + upsert = true, + update = SearchCacheEntry(request, newPackages) + ) val packagesCache = packagesCacheCollection.await() newPackages.forEach { - packagesCache.insert(PackageCacheEntry(it.idHash, it)) + packagesCache.updateWhere( + fieldSelector = PackageCacheEntry::key.name, + fieldValue = it.idHash, + upsert = true, + update = PackageCacheEntry(it.idHash, it) + ) } } @@ -354,11 +366,21 @@ public class PackageSearchApiClient( url = endpoints.startScroll, body = request, requestBuilder = requestBuilder, - ).also { - scrollCache.insert(ScrollRequestCacheEntry(request, it)) + ).also { it -> + scrollCache.updateWhere( + fieldSelector = ScrollRequestCacheEntry::key.name, + fieldValue = request, + upsert = true, + update = ScrollRequestCacheEntry(request, it) + ) val packagesCache = packagesCacheCollection.await() it.data.forEach { - packagesCache.insert(PackageCacheEntry(it.idHash, it)) + packagesCache.updateWhere( + fieldSelector = PackageCacheEntry::key.name, + fieldValue = it.idHash, + upsert = true, + update = PackageCacheEntry(it.idHash, it) + ) } } } @@ -390,7 +412,12 @@ public class PackageSearchApiClient( body = request, requestBuilder = requestBuilder, ).also { - apiProjectsCache.insert(ApiProjectCacheEntry(request, it)) + apiProjectsCache.updateWhere( + fieldSelector = ApiProjectCacheEntry::key.name, + fieldValue = request, + upsert = true, + update = ApiProjectCacheEntry(request, it) + ) } } @@ -410,7 +437,12 @@ public class PackageSearchApiClient( val packagesCache = packagesCacheCollection.await() results.forEach { - packagesCache.insert(PackageCacheEntry(it.idHash, it)) + packagesCache.updateWhere( + fieldSelector = PackageCacheEntry::key.name, + fieldValue = it.idHash, + upsert = true, + update = PackageCacheEntry(it.idHash, it) + ) } return results diff --git a/http/client/src/jvmTest/kotlin/CacheTests.kt b/http/client/src/jvmTest/kotlin/CacheTests.kt index 98a87eb..9c3d193 100644 --- a/http/client/src/jvmTest/kotlin/CacheTests.kt +++ b/http/client/src/jvmTest/kotlin/CacheTests.kt @@ -1,10 +1,10 @@ -import cache.ApiPackageCacheEntry -import cache.CacheDB import io.ktor.client.engine.mock.MockEngine +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import kotlinx.datetime.Clock import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiRepository +import org.jetbrains.packagesearch.api.v3.http.PackageCacheEntry import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints import org.jetbrains.packagesearch.api.v3.http.RefreshPackagesInfoRequest @@ -18,6 +18,7 @@ import kotlin.collections.map import kotlin.collections.plus import kotlin.collections.toSet import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -27,11 +28,10 @@ class CacheTests { val apiClient: PackageSearchApiClient, val mockEngine: MockEngine, val mockResponse: T, - val db: CacheDB ) @Test - fun `PackageByID test Cache Hit`() = runTest(timeout = 10.seconds) { + fun `PackageByID test Cache Hit`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine, mockResponse) = setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") @@ -47,7 +47,7 @@ class CacheTests { // Ensure the mock engine wasn't called a second time. val endpointsCalls = - mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds) + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIdHashes) mockEngine.close() assertEquals(1, endpointsCalls) @@ -55,38 +55,50 @@ class CacheTests { @Test - fun `PackageByID test Cache Expired`() = runTest(timeout = 20.seconds) { + fun `PackageByID test Cache Expired`() = runTest(timeout = 30.minutes) { val testEnv = setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") - val expiredDBEntry = ApiPackageCacheEntry( - apiPackage = testEnv.mockResponse.first(), - expires = Clock.System.now().minus(1.minutes), - ) + suspend fun getDBEntries() = testEnv.apiClient.packagesCacheCollection.await().iterateAll().toList() + + val apiPackage = testEnv.mockResponse.first() + + val expiredDBEntry = PackageCacheEntry(apiPackage.idHash, apiPackage, Clock.System.now().minus(7.days)) - testEnv.db.apiPackagesCache().insert(expiredDBEntry) + var elementInsideDB = getDBEntries() + assert(elementInsideDB.isEmpty()) - val packageId = testEnv.mockResponse.first().id + + testEnv.apiClient.packagesCacheCollection.await().insert(expiredDBEntry) + elementInsideDB = getDBEntries() + assert(elementInsideDB.size == 1) + + + val packageId = apiPackage.id // First request (should refresh cache) val response1 = testEnv.apiClient.getPackageInfoByIds(setOf(packageId)) assert(response1.values.firstOrNull()!!.id == packageId) + elementInsideDB = getDBEntries() + assert(elementInsideDB.size == 1) // Ensure data has been retrieved from BE. - assertEquals(1, testEnv.mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) + assertEquals(1, testEnv.mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIdHashes)) // Second request (should hit cache) val response2 = testEnv.apiClient.getPackageInfoByIds(setOf(packageId)) assert(response2.values.firstOrNull()!!.id == packageId) + elementInsideDB = getDBEntries() + assert(elementInsideDB.size == 1) testEnv.mockEngine.close() - assertEquals(1, testEnv.mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds)) + assertEquals(1, testEnv.mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIdHashes)) } @Test - fun `PackageByHash test Cache Hit`() = runTest(timeout = 10.seconds) { + fun `PackageByHash test Cache Hit`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine, mockResponse) = setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") @@ -108,7 +120,7 @@ class CacheTests { } @Test - fun `PackageById test cache not exhaustive`() = runTest(timeout = 10.seconds) { + fun `PackageById test cache not exhaustive`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine, mockResponse) = setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") //create cache entry @@ -117,14 +129,14 @@ class CacheTests { apiClient.getPackageInfoByIds(mockResponse.map { it.id }.toSet() + "androidx.compose.runtime:runtime") val endpointsCalls = - mockEngine.geRequestsFor(PackageSearchEndpoints.DEV.packageInfoByIds) + mockEngine.geRequestsFor(PackageSearchEndpoints.DEV.packageInfoByIdHashes) mockEngine.close() assertEquals(2, endpointsCalls.size) } @Test - fun `getKnownRepositories test Cache Hit`() = runTest(timeout = 10.seconds) { + fun `getKnownRepositories test Cache Hit`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine) = setupTestEnv>("known-repositories.json") val response1 = apiClient.getKnownRepositories() @@ -145,7 +157,7 @@ class CacheTests { } @Test - fun `searchPackages test Cache Hit`() = runTest(timeout = 10.seconds) { + fun `searchPackages test Cache Hit`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine) = setupTestEnv>("package-info-by-ids-ktor.json") val queryString = "whatever" repeat(3) { @@ -165,7 +177,7 @@ class CacheTests { } @Test - fun `searchPackages non exhaustive cache`() = runTest(timeout = 10.seconds) { + fun `searchPackages non exhaustive cache`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine) = setupTestEnv>("package-info-by-ids-ktor.json") val queryString = "whatever" apiClient.searchPackages( @@ -190,7 +202,7 @@ class CacheTests { } @Test - fun `refreshPackagesInfo smart cache`() = runTest(timeout = 10.seconds) { + fun `refreshPackagesInfo smart cache`() = runTest(timeout = 30.seconds) { val (apiClient, mockEngine, mockResponse) = setupTestEnv>("package-info-by-ids-ktor.json") val refreshRequest = RefreshPackagesInfoRequest( @@ -211,7 +223,7 @@ class CacheTests { apiClient.getPackageInfoByIds(setOf(mockResponse.first().id)) val endpointsCalls = - mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIds) + mockEngine.getRequestCount(PackageSearchEndpoints.DEV.packageInfoByIdHashes) mockEngine.close() assertEquals(0, endpointsCalls) diff --git a/http/client/src/jvmTest/kotlin/Utils.kt b/http/client/src/jvmTest/kotlin/Utils.kt index abeeffd..50728d2 100644 --- a/http/client/src/jvmTest/kotlin/Utils.kt +++ b/http/client/src/jvmTest/kotlin/Utils.kt @@ -1,5 +1,4 @@ import CacheTests.TestEnv -import cache.CacheDB import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.MockEngine.Companion.invoke @@ -38,8 +37,6 @@ internal inline fun TestScope.setupTestEnv( val mockEngine = buildMockEngine(jsonResponse) - val dataStore = MVStore.open(null).asDataStore() - val apiClient = PackageSearchApiClient( httpClient = setupHttpClient(mockEngine), endpoints = PackageSearchEndpoints.DEV, @@ -50,7 +47,6 @@ internal inline fun TestScope.setupTestEnv( apiClient = apiClient, mockEngine = mockEngine, mockResponse = mockResponse, - db = CacheDB(dataStore) ) } diff --git a/settings.gradle.kts b/settings.gradle.kts index b2d6a09..d5eedbd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,7 @@ dependencyResolutionManagement { from(files("packagesearch-api-models.versions.toml")) } create("kotlinxDocumentStore") { - from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev5") + from("com.github.lamba92:kotlinx-document-store-version-catalog:0.0.1-dev7") } } } From af7328015ceac8dec893a12666ac791e8559c8fe Mon Sep 17 00:00:00 2001 From: fscarponi Date: Thu, 25 Jul 2024 13:09:44 +0200 Subject: [PATCH 24/27] clarify yml test info --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8c7ee2..e3d8e95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: Test with Cache +name: Test APIClient Cache on: push: @@ -19,5 +19,5 @@ jobs: with: distribution: 'adopt' java-version: '17' - - name: Run tests for cache system + - name: Run Test APIClient Cache run: ./gradlew :http:client:jvmTest --tests "CacheTests" \ No newline at end of file From fa6e70d746bc6f17befe717b8cc3a90003fefecd Mon Sep 17 00:00:00 2001 From: fscarponi Date: Thu, 25 Jul 2024 13:10:50 +0200 Subject: [PATCH 25/27] fix --- http/client/src/jvmTest/kotlin/CacheTests.kt | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/http/client/src/jvmTest/kotlin/CacheTests.kt b/http/client/src/jvmTest/kotlin/CacheTests.kt index 9c3d193..b8144e5 100644 --- a/http/client/src/jvmTest/kotlin/CacheTests.kt +++ b/http/client/src/jvmTest/kotlin/CacheTests.kt @@ -1,25 +1,14 @@ -import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.* import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import kotlinx.datetime.Clock import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiRepository -import org.jetbrains.packagesearch.api.v3.http.PackageCacheEntry -import org.jetbrains.packagesearch.api.v3.http.PackageSearchApiClient -import org.jetbrains.packagesearch.api.v3.http.PackageSearchEndpoints -import org.jetbrains.packagesearch.api.v3.http.RefreshPackagesInfoRequest -import org.jetbrains.packagesearch.api.v3.http.SearchPackagesRequest +import org.jetbrains.packagesearch.api.v3.http.* import org.jetbrains.packagesearch.api.v3.search.PackagesType import org.junit.jupiter.api.Test -import kotlin.collections.first -import kotlin.collections.firstOrNull -import kotlin.collections.forEach -import kotlin.collections.map -import kotlin.collections.plus -import kotlin.collections.toSet import kotlin.test.assertEquals import kotlin.time.Duration.Companion.days -import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds class CacheTests { @@ -55,7 +44,7 @@ class CacheTests { @Test - fun `PackageByID test Cache Expired`() = runTest(timeout = 30.minutes) { + fun `PackageByID test Cache Expired`() = runTest(timeout = 30.seconds) { val testEnv = setupTestEnv>(resourceFilename = "package-info-by-ids-ktor.json") From ce5877151589ae89e620ed38eeda181a1b45785b Mon Sep 17 00:00:00 2001 From: fscarponi Date: Fri, 26 Jul 2024 17:39:24 +0200 Subject: [PATCH 26/27] Refactor cache expiration logic and improve code readability This commit refactors and standardizes cache expiration logic across various functions by making `cacheDuration` an explicit parameter. It also enhances code readability and maintainability by removing redundant variables and ensuring the consistent use of `await` for asynchronous operations. Additionally, it introduces private helper functions and fields to better organize the offline state and cache interaction logic. --- build.gradle.kts | 6 +- http/client/build.gradle.kts | 4 +- .../commonMain/kotlin/cache/CacheEntries.kt | 2 +- .../api/v3/http/PackageSearchApiClient.kt | 78 ++++++++++++------- 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ec624c1..876835d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,12 +19,16 @@ kotlin { api(kotlinxDocumentStore.browser) } } + jvmMain{ + dependencies{ + api(kotlinxDocumentStore.mvstore) + } + } jvmTest { dependencies { implementation(packageSearchApiModelsVersions.junit.jupiter.api) implementation(packageSearchApiModelsVersions.junit.jupiter.params) implementation(packageSearchApiModelsVersions.assertk) - implementation(kotlinxDocumentStore.mvstore) runtimeOnly(packageSearchApiModelsVersions.junit.jupiter.engine) } } diff --git a/http/client/build.gradle.kts b/http/client/build.gradle.kts index 740737a..ac0a2a7 100644 --- a/http/client/build.gradle.kts +++ b/http/client/build.gradle.kts @@ -25,8 +25,8 @@ kotlin { api(packageSearchApiModelsVersions.kotlinx.serialization.json) } } - commonTest{ - dependencies{ + commonTest { + dependencies { api(packageSearchApiModelsVersions.kotlinx.coroutines.test) api(packageSearchApiModelsVersions.ktor.client.mock) api(packageSearchApiModelsVersions.ktor.client.logging) diff --git a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt index 2416237..6605e75 100644 --- a/http/client/src/commonMain/kotlin/cache/CacheEntries.kt +++ b/http/client/src/commonMain/kotlin/cache/CacheEntries.kt @@ -16,7 +16,7 @@ internal data class CacheEntry( val createdAt: Instant = Clock.System.now() ) { - internal fun isExpired(expirationTime: Duration = DEFAULT_EXPIRATION_TIME): Boolean = + internal fun isExpired(expirationTime: Duration): Boolean = createdAt + expirationTime < Clock.System.now() diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index 380cf93..ea9f407 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -29,6 +29,7 @@ import kotlinx.serialization.protobuf.ProtoBuf import org.jetbrains.packagesearch.api.v3.ApiPackage import org.jetbrains.packagesearch.api.v3.ApiProject import org.jetbrains.packagesearch.api.v3.ApiRepository +import org.jetbrains.packagesearch.packageversionutils.normalization.NormalizedVersion import kotlin.collections.map import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes @@ -44,9 +45,9 @@ public class PackageSearchApiClient( dataStore: DataStore, private val httpClient: HttpClient = defaultHttpClient(), public val endpoints: PackageSearchEndpoints, - coroutineScope: CoroutineScope = CoroutineScope(httpClient.coroutineContext), + coroutineScope: CoroutineScope = httpClient, onlineCheckInterval: Duration = 1.minutes, - public val cacheDuration: Duration = DEFAULT_EXPIRATION_TIME, + private val cacheDuration: Duration = DEFAULT_EXPIRATION_TIME, ) { private val kotlinxDb = KotlinxDocumentDatabase(dataStore) @@ -76,7 +77,6 @@ public class PackageSearchApiClient( .apply { createIndex(ApiProjectCacheEntry::key.name) } } - public val onlineStateFlow: StateFlow = flow { while (true) { @@ -90,6 +90,8 @@ public class PackageSearchApiClient( .retry() .stateIn(coroutineScope, SharingStarted.Lazily, true) + private val isOffline get() = !onlineStateFlow.value + @Serializable private data class Error(val error: Inner) { @Serializable @@ -192,14 +194,14 @@ public class PackageSearchApiClient( public suspend fun getKnownRepositories(requestBuilder: (HttpRequestBuilder.() -> Unit)? = null): List { val apiRepositoryCache = repositoriesCacheCollection.await() - val isOffline = !onlineStateFlow.value + val cachedResult = apiRepositoryCache.find( selector = MiscellaneousCacheEntry::key.name, value = "repositories" ).singleOrNull() cachedResult?.let { - if (isOffline || !it.isExpired()) { + if (isOffline || !it.isExpired(cacheDuration)) { return apiRepositoryCache.json.decodeFromJsonElement>(it.value) } @@ -240,15 +242,11 @@ public class PackageSearchApiClient( requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): Map = coroutineScope { - val packagesCache = packagesCacheCollection.await() - - val isOffline = !onlineStateFlow.value - val cachedResults = idHashes .map { id -> // NOTE: id depends on the lookupField async { - packagesCache + packagesCacheCollection.await() .find(PackageCacheEntry::key.name, value = id) .firstOrNull() } @@ -286,7 +284,7 @@ public class PackageSearchApiClient( val notFoundPackages = idHashes - cachedResults.map { it.key }.toSet() - onlineResults.keys notFoundPackages.forEach { - packagesCache.updateWhere( + packagesCacheCollection.await().updateWhere( fieldSelector = PackageCacheEntry::key.name, fieldValue = it, upsert = true, @@ -295,7 +293,7 @@ public class PackageSearchApiClient( } onlineResults.values.forEach { - packagesCache.updateWhere( + packagesCacheCollection.await().updateWhere( fieldSelector = PackageCacheEntry::key.name, fieldValue = it.idHash, upsert = true, @@ -313,13 +311,13 @@ public class PackageSearchApiClient( request: SearchPackagesRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): List { - val isOffline = !onlineStateFlow.value + val searchCache = searchCacheCollection.await() searchCache.find(selector = SearchCacheEntry::key.name, value = request) .firstOrNull() - ?.takeIf { isOffline || !it.isExpired() } + ?.takeIf { isOffline || !it.isExpired(cacheDuration) } ?.also { return it.value } // cache result not found or expired or not exhaustive enough @@ -356,10 +354,7 @@ public class PackageSearchApiClient( val scrollCache = scrollRequestCacheCollection.await() scrollCache.find(selector = ScrollRequestCacheEntry::key.name, value = request) - .firstOrNull() - ?.also { - if (!it.isExpired()) return it.value - } + .firstOrNull()?.also { if (!it.isExpired(cacheDuration)) return it.value } return defaultRequest<_, SearchPackagesScrollResponse>( method = HttpMethod.Post, @@ -403,8 +398,8 @@ public class PackageSearchApiClient( val apiProjectsCache = apiProjectCacheCollection.await() - apiProjectsCache.find(selector = SearchCacheEntry::key.name, value = request).firstOrNull() - ?.also { if (!it.isExpired()) return it.value } + apiProjectsCache.find(selector = SearchCacheEntry::key.name, value = request) + .firstOrNull()?.also { if (!it.isExpired(cacheDuration)) return it.value } return defaultRequest<_, List>( method = HttpMethod.Post, @@ -426,17 +421,47 @@ public class PackageSearchApiClient( request: RefreshPackagesInfoRequest, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, ): List { - //no caches for this endpoint - val results = + //gather results from cache + val packagesCache = packagesCacheCollection.await() + + val cachedResults = request.packages + .associate { it.packageIdHash to it.latestKnownVersion } + .mapKeys { packagesCache.find(PackageCacheEntry::key.name, it).firstOrNull() } + .filterKeys { it != null }.mapKeys { it.key!! } + .map { (cacheEntry, versionName) -> + when { + versionName == null -> cacheEntry // no version specified so any version is fine + cacheEntry.value == null -> cacheEntry // We don't know this package at all, so the cached result is ok + + NormalizedVersion.from(versionName) < cacheEntry.value.versions.latest.normalized -> + cacheEntry //we know that the version is outdated, so we need to refresh it + + NormalizedVersion.from(versionName) > cacheEntry.value.versions.latest.normalized -> + PackageCacheEntry(cacheEntry.key, null)//we know that there are no other new versions, + // so we can omit this package for online request + + else -> null + } + }.filterNotNull() + + + val cachedIdHashes = cachedResults.map { it.key } + val notFoundPackages = request.packages.filter { it.packageIdHash !in cachedIdHashes } + + if (isOffline || notFoundPackages.isEmpty()) { + return cachedResults.mapNotNull { it.value } + } + + + val onlineResults = defaultRequest<_, List>( method = HttpMethod.Post, url = endpoints.refreshPackagesInfo, - body = request, + body = RefreshPackagesInfoRequest(notFoundPackages), requestBuilder = requestBuilder, ) - val packagesCache = packagesCacheCollection.await() - results.forEach { + onlineResults.forEach { packagesCache.updateWhere( fieldSelector = PackageCacheEntry::key.name, fieldValue = it.idHash, @@ -444,8 +469,7 @@ public class PackageSearchApiClient( update = PackageCacheEntry(it.idHash, it) ) } - return results - + return cachedResults.mapNotNull { it.value } + onlineResults } } From 9926f77e220d596c348a079f40dd80749522d265 Mon Sep 17 00:00:00 2001 From: fscarponi Date: Fri, 26 Jul 2024 18:21:49 +0200 Subject: [PATCH 27/27] adjust apis to not break compatibility --- .../packagesearch/api/v3/http/PackageSearchApiClient.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt index ea9f407..57d574d 100644 --- a/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt +++ b/http/client/src/commonMain/kotlin/org/jetbrains/packagesearch/api/v3/http/PackageSearchApiClient.kt @@ -237,6 +237,11 @@ public class PackageSearchApiClient( ) public suspend fun getPackageInfoByIdHashes( + idHashes: Set, + requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, + ): Map = getPackageInfoByIdHashes(idHashes, true, requestBuilder) + + private suspend fun getPackageInfoByIdHashes( idHashes: Set, useHashes: Boolean = true, requestBuilder: (HttpRequestBuilder.() -> Unit)? = null, @@ -266,7 +271,7 @@ public class PackageSearchApiClient( val resultIds = validResults.map { it.key } val unresolvedIdentifiers = - idHashes - resultIds + idHashes - resultIds.toSet() if (unresolvedIdentifiers.isEmpty()) { return@coroutineScope validResults