diff --git a/CHANGELOG.md b/CHANGELOG.md index 100b1b24d..96b2297f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.6.1 +> Published 11 April 2025 + +### Bug fixes 🐛 +* Fix bidirectional flows in non-suspend streams by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/316 +* Fix KRPC-173 (#315) by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/317 + +**Full Changelog**: https://github.com/Kotlin/kotlinx-rpc/compare/0.6.0...0.6.1 + # 0.6.0 > Published 4 April 2025 diff --git a/gradle-conventions-settings/develocity/build.gradle.kts b/gradle-conventions-settings/develocity/build.gradle.kts index 315960dca..a5ad90b45 100644 --- a/gradle-conventions-settings/develocity/build.gradle.kts +++ b/gradle-conventions-settings/develocity/build.gradle.kts @@ -16,5 +16,5 @@ configurations.configureEach { dependencies { implementation("com.gradle:develocity-gradle-plugin:3.17") - implementation("com.gradle:common-custom-user-data-gradle-plugin:2.1") + implementation("com.gradle:common-custom-user-data-gradle-plugin:2.2.1") } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 15544b034..2b04615cd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,6 +4,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://packages.jetbrains.team/files/p/krpc/build-deps/distributions/gradle-8.12.1-bin.zip +distributionUrl=https\://packages.jetbrains.team/files/p/krpc/build-deps/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/monitor/requirements.txt b/monitor/requirements.txt index 5b90c5b37..8e5b39d68 100644 --- a/monitor/requirements.txt +++ b/monitor/requirements.txt @@ -1,5 +1,5 @@ aiohappyeyeballs==2.6.1 -aiohttp==3.11.14 +aiohttp==3.11.16 aiohttp-jinja2==1.6 aiosignal==1.3.2 anyio==4.9.0 @@ -20,15 +20,15 @@ MarkupSafe==3.0.2 mdit-py-plugins==0.4.2 mdurl==0.1.2 msgpack==1.1.0 -multidict==6.1.0 -platformdirs==4.3.6 -propcache==0.3.0 +multidict==6.4.2 +platformdirs==4.3.7 +propcache==0.3.1 Pygments==2.19.1 -rich==13.9.4 +rich==14.0.0 sniffio==1.3.1 -textual==2.1.2 +textual==3.0.1 textual-dev==1.7.0 textual-serve==1.1.1 -typing_extensions==4.12.2 +typing_extensions==4.13.1 uc-micro-py==1.0.3 -yarl==1.18.3 +yarl==1.19.0 diff --git a/renovate.json5 b/renovate.json5 index 347b07976..7ffeed47b 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -53,6 +53,15 @@ ], "enabled": false, }, + { + // Reason: kotlin-dsl updates are to be done manually + "description": "Ignore Kotlin DSL (Gradle) updates", + "matchDepNames": [ + "/org.gradle.kotlin:gradle-kotlin-dsl-plugins(:|\\.).*/", + "/org.gradle.kotlin.kotlin-dsl(:|\\.).*/", + ], + "enabled": false, + }, { // Reason: we support java 8, logback 1.4 or later switched to java 11 "description": "Ignore logback in Core", diff --git a/samples/grpc-app/build.gradle.kts b/samples/grpc-app/build.gradle.kts index 4d9f4d488..3d85034fa 100644 --- a/samples/grpc-app/build.gradle.kts +++ b/samples/grpc-app/build.gradle.kts @@ -5,8 +5,8 @@ plugins { kotlin("jvm") version "2.1.10" kotlin("plugin.serialization") version "2.1.10" - id("org.jetbrains.kotlinx.rpc.plugin") version "0.5.1-grpc-16" - id("com.google.protobuf") version "0.9.4" + id("org.jetbrains.kotlinx.rpc.plugin") version "0.6.0" + id("com.google.protobuf") version "0.9.5" } group = "kotlinx.rpc.sample" @@ -22,9 +22,9 @@ kotlin { } dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-rpc-grpc-core:0.5.1-grpc-16") - implementation("ch.qos.logback:logback-classic:1.5.16") - implementation("io.grpc:grpc-netty:1.69.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-grpc-core:0.5.1-grpc-39") + implementation("ch.qos.logback:logback-classic:1.5.18") + implementation("io.grpc:grpc-netty:1.71.0") } rpc { diff --git a/samples/grpc-app/gradle/wrapper/gradle-wrapper.jar b/samples/grpc-app/gradle/wrapper/gradle-wrapper.jar index a4b76b953..9bbc975c7 100644 Binary files a/samples/grpc-app/gradle/wrapper/gradle-wrapper.jar and b/samples/grpc-app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/grpc-app/gradle/wrapper/gradle-wrapper.properties b/samples/grpc-app/gradle/wrapper/gradle-wrapper.properties index b2f467f62..37f853b1c 100644 --- a/samples/grpc-app/gradle/wrapper/gradle-wrapper.properties +++ b/samples/grpc-app/gradle/wrapper/gradle-wrapper.properties @@ -1,10 +1,6 @@ -# -# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -# - distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/samples/grpc-app/gradlew b/samples/grpc-app/gradlew index 89f1e740c..faf93008b 100755 --- a/samples/grpc-app/gradlew +++ b/samples/grpc-app/gradlew @@ -1,7 +1,21 @@ #!/bin/sh # -# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 # ############################################################################## @@ -191,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/samples/ktor-all-platforms-app/composeApp/build.gradle.kts b/samples/ktor-all-platforms-app/composeApp/build.gradle.kts index e51d28ac1..0dd4ada29 100644 --- a/samples/ktor-all-platforms-app/composeApp/build.gradle.kts +++ b/samples/ktor-all-platforms-app/composeApp/build.gradle.kts @@ -13,6 +13,7 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsCompose) alias(libs.plugins.compose.compiler) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/samples/ktor-all-platforms-app/composeApp/src/commonMain/kotlin/App.kt b/samples/ktor-all-platforms-app/composeApp/src/commonMain/kotlin/App.kt index 6f63073b4..e159d8c6d 100644 --- a/samples/ktor-all-platforms-app/composeApp/src/commonMain/kotlin/App.kt +++ b/samples/ktor-all-platforms-app/composeApp/src/commonMain/kotlin/App.kt @@ -66,10 +66,8 @@ fun App() { } LaunchedEffect(service) { - streamScoped { - service.subscribeToNews().collect { article -> - news.add(article) - } + service.subscribeToNews().collect { article -> + news.add(article) } } diff --git a/samples/ktor-all-platforms-app/gradle/libs.versions.toml b/samples/ktor-all-platforms-app/gradle/libs.versions.toml index 906c1cb14..82ac1dc36 100644 --- a/samples/ktor-all-platforms-app/gradle/libs.versions.toml +++ b/samples/ktor-all-platforms-app/gradle/libs.versions.toml @@ -1,25 +1,25 @@ [versions] -kotlin = "2.1.10" +kotlin = "2.1.20" -agp = "8.8.0-alpha05" +agp = "8.9.1" android-compileSdk = "35" android-minSdk = "24" android-targetSdk = "35" -androidx-activityCompose = "1.9.3" +androidx-activityCompose = "1.10.1" androidx-appcompat = "1.7.0" -androidx-constraintlayout = "2.2.0" -androidx-core-ktx = "1.15.0" +androidx-constraintlayout = "2.2.1" +androidx-core-ktx = "1.16.0" androidx-espresso-core = "3.6.1" androidx-material = "1.12.0" androidx-test-junit = "1.2.1" -compose = "1.7.6" -compose-plugin = "1.8.0-alpha01" +compose = "1.7.8" +compose-plugin = "1.8.0-SNAPSHOT+pull-5245" junit = "4.13.2" -ktor = "3.0.3" -logback = "1.5.16" -serialization = "1.8.0" -coroutines = "1.10.1" -kotlinx-rpc = "0.5.1" +ktor = "3.1.2" +logback = "1.5.18" +serialization = "1.8.1" +coroutines = "1.10.2" +kotlinx-rpc = "0.6.1" [libraries] # kotlin diff --git a/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.jar b/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.jar index a4b76b953..9bbc975c7 100644 Binary files a/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.jar and b/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.properties b/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.properties index cea7a793a..37f853b1c 100644 --- a/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.properties +++ b/samples/ktor-all-platforms-app/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/samples/ktor-all-platforms-app/gradlew b/samples/ktor-all-platforms-app/gradlew index f3b75f3b0..faf93008b 100755 --- a/samples/ktor-all-platforms-app/gradlew +++ b/samples/ktor-all-platforms-app/gradlew @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/samples/ktor-all-platforms-app/server/build.gradle.kts b/samples/ktor-all-platforms-app/server/build.gradle.kts index a65847c32..396144121 100644 --- a/samples/ktor-all-platforms-app/server/build.gradle.kts +++ b/samples/ktor-all-platforms-app/server/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.kotlinJvm) alias(libs.plugins.ktor) + alias(libs.plugins.kotlinx.rpc) application } diff --git a/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/UserServiceImpl.kt b/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/UserServiceImpl.kt index 3ffd652c2..89724005b 100644 --- a/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/UserServiceImpl.kt +++ b/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/UserServiceImpl.kt @@ -16,7 +16,7 @@ class UserServiceImpl(override val coroutineContext: CoroutineContext) : UserSer return "Nice to meet you $user, how is it in ${userData.address}?" } - override suspend fun subscribeToNews(): Flow { + override fun subscribeToNews(): Flow { return flow { repeat(10) { delay(300) diff --git a/samples/ktor-all-platforms-app/server/src/test/kotlin/kotlinx/rpc/sample/ApplicationTest.kt b/samples/ktor-all-platforms-app/server/src/test/kotlin/kotlinx/rpc/sample/ApplicationTest.kt index 80440fd8f..d3ce2155a 100644 --- a/samples/ktor-all-platforms-app/server/src/test/kotlin/kotlinx/rpc/sample/ApplicationTest.kt +++ b/samples/ktor-all-platforms-app/server/src/test/kotlin/kotlinx/rpc/sample/ApplicationTest.kt @@ -35,11 +35,9 @@ class ApplicationTest { actual = service.hello("Alex", UserData("address1", "last")), ) - streamScoped { - assertEquals( - expected = List(10) { "Article number $it" }, - actual = service.subscribeToNews().toList(), - ) - } + assertEquals( + expected = List(10) { "Article number $it" }, + actual = service.subscribeToNews().toList(), + ) } } diff --git a/samples/ktor-all-platforms-app/shared/src/commonMain/kotlin/UserService.kt b/samples/ktor-all-platforms-app/shared/src/commonMain/kotlin/UserService.kt index 91ed09431..8ea4be9a3 100644 --- a/samples/ktor-all-platforms-app/shared/src/commonMain/kotlin/UserService.kt +++ b/samples/ktor-all-platforms-app/shared/src/commonMain/kotlin/UserService.kt @@ -17,5 +17,5 @@ data class UserData( interface UserService : RemoteService { suspend fun hello(user: String, userData: UserData): String - suspend fun subscribeToNews(): Flow + fun subscribeToNews(): Flow } diff --git a/samples/ktor-android-app/app/build.gradle.kts b/samples/ktor-android-app/app/build.gradle.kts index 78b2f5a29..69692fe18 100644 --- a/samples/ktor-android-app/app/build.gradle.kts +++ b/samples/ktor-android-app/app/build.gradle.kts @@ -7,6 +7,7 @@ plugins { alias(libs.plugins.kotlinAndroid) alias(libs.plugins.kotlinPluginSerialization) alias(libs.plugins.compose.compiler) + alias(libs.plugins.kotlinx.rpc) } android { diff --git a/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/ui/AppViewModel.kt b/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/ui/AppViewModel.kt index 43ee0fc08..65a80c57b 100644 --- a/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/ui/AppViewModel.kt +++ b/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/ui/AppViewModel.kt @@ -39,27 +39,23 @@ class AppViewModel : ViewModel() { private fun fetchData() { viewModelScope.launch(Dispatchers.IO) { - streamScoped { - delay(2000) - val greetingDeferred = async { - apiService?.hello( - "Alex", - UserData("Berlin", "Smith") - ) - } - val newsDeferred = async { apiService?.subscribeToNews() } + delay(2000) + val greetingDeferred = async { + apiService?.hello( + "Alex", + UserData("Berlin", "Smith") + ) + } - val serverGreeting = greetingDeferred.await() - val news = newsDeferred.await() + val serverGreeting = greetingDeferred.await() - val allNews: MutableList = mutableListOf() - news?.collect { - allNews += it + val allNews: MutableList = mutableListOf() + apiService?.subscribeToNews()?.collect { + allNews += it - val sendNews = allNews.toMutableList() // fix ConcurrentModificationException - serverGreeting?.let { - _uiState.value = WelcomeData(serverGreeting, sendNews) - } + val sendNews = allNews.toMutableList() // fix ConcurrentModificationException + serverGreeting?.let { + _uiState.value = WelcomeData(serverGreeting, sendNews) } } } diff --git a/samples/ktor-android-app/common/src/main/kotlin/kotlinx/rpc/sample/MyService.kt b/samples/ktor-android-app/common/src/main/kotlin/kotlinx/rpc/sample/MyService.kt index 8fd7d9b0f..a5bf328a9 100644 --- a/samples/ktor-android-app/common/src/main/kotlin/kotlinx/rpc/sample/MyService.kt +++ b/samples/ktor-android-app/common/src/main/kotlin/kotlinx/rpc/sample/MyService.kt @@ -12,5 +12,5 @@ import kotlinx.rpc.annotations.Rpc interface MyService : RemoteService { suspend fun hello(user: String, userData: UserData): String - suspend fun subscribeToNews(): Flow + fun subscribeToNews(): Flow } diff --git a/samples/ktor-android-app/gradle/libs.versions.toml b/samples/ktor-android-app/gradle/libs.versions.toml index 2db1303d8..c2d4fccaa 100644 --- a/samples/ktor-android-app/gradle/libs.versions.toml +++ b/samples/ktor-android-app/gradle/libs.versions.toml @@ -1,21 +1,21 @@ [versions] -agp = "8.8.0" -kotlin = "2.1.10" -androidx-activityCompose = "1.9.3" +agp = "8.9.1" +kotlin = "2.1.20" +androidx-activityCompose = "1.10.1" androidx-appcompat = "1.7.0" -androidx-constraintlayout = "2.2.0" -androidx-core-ktx = "1.15.0" +androidx-constraintlayout = "2.2.1" +androidx-core-ktx = "1.16.0" androidx-test-junit = "1.2.1" -compose = "1.7.6" +compose = "1.7.8" compose-plugin = "1.5.14" # https://mvnrepository.com/artifact/androidx.compose.compiler/compiler -compose-bom = "2024.12.01" -material3 = "1.3.1" +compose-bom = "2025.04.00" +material3 = "1.3.2" junit = "4.13.2" -ktor = "3.0.3" -kotlinx-serialization-json = "1.8.0" -kotlinx-coroutines-core = "1.10.1" -logback = "1.5.16" -kotlinx-rpc = "0.5.1" +ktor = "3.1.2" +kotlinx-serialization-json = "1.8.1" +kotlinx-coroutines-core = "1.10.2" +logback = "1.5.18" +kotlinx-rpc = "0.6.1" [libraries] # kotlin diff --git a/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.jar b/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.jar index a4b76b953..9bbc975c7 100644 Binary files a/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.jar and b/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.properties b/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.properties index cea7a793a..37f853b1c 100644 --- a/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.properties +++ b/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/samples/ktor-android-app/gradlew b/samples/ktor-android-app/gradlew index f3b75f3b0..faf93008b 100755 --- a/samples/ktor-android-app/gradlew +++ b/samples/ktor-android-app/gradlew @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/samples/ktor-android-app/server/build.gradle.kts b/samples/ktor-android-app/server/build.gradle.kts index b30cf67db..fccdeea66 100644 --- a/samples/ktor-android-app/server/build.gradle.kts +++ b/samples/ktor-android-app/server/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.kotlinJvm) alias(libs.plugins.kotlinPluginSerialization) alias(libs.plugins.ktor) + alias(libs.plugins.kotlinx.rpc) application distribution diff --git a/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/MyServiceImpl.kt b/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/MyServiceImpl.kt index e63c5bb5e..453dee6f3 100644 --- a/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/MyServiceImpl.kt +++ b/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/MyServiceImpl.kt @@ -14,10 +14,10 @@ class MyServiceImpl(override val coroutineContext: CoroutineContext) : MyService return "Nice to meet you $user, how is it in ${userData.address}?" } - override suspend fun subscribeToNews(): Flow { + override fun subscribeToNews(): Flow { return flow { repeat(10) { - delay(2000) + delay(200) emit("Article number $it") } } diff --git a/samples/ktor-android-app/server/src/test/kotlin/ApplicationTest.kt b/samples/ktor-android-app/server/src/test/kotlin/ApplicationTest.kt index a706b3b6b..9d09f1a3f 100644 --- a/samples/ktor-android-app/server/src/test/kotlin/ApplicationTest.kt +++ b/samples/ktor-android-app/server/src/test/kotlin/ApplicationTest.kt @@ -13,16 +13,20 @@ import kotlinx.rpc.krpc.ktor.client.installKrpc import kotlinx.rpc.krpc.ktor.client.rpc import kotlinx.rpc.krpc.ktor.client.rpcConfig import kotlinx.rpc.krpc.serialization.json.json -import kotlinx.rpc.krpc.streamScoped import kotlinx.rpc.withService import kotlinx.rpc.sample.MyService import kotlinx.rpc.sample.UserData +import kotlinx.rpc.sample.module import kotlin.test.Test import kotlin.test.assertEquals class ApplicationTest { @Test fun testRoot() = testApplication { + application { + module() + } + val service = createClient { installKrpc() }.rpc("/api") { @@ -38,12 +42,10 @@ class ApplicationTest { actual = service.hello("Alex", UserData("address1", "last")), ) - streamScoped { - assertEquals( - expected = List(10) { "Article number $it" }, - actual = service.subscribeToNews().toList(), - ) - } + assertEquals( + expected = List(10) { "Article number $it" }, + actual = service.subscribeToNews().toList(), + ) } @Test diff --git a/samples/ktor-web-app/common/src/commonMain/kotlin/Common.kt b/samples/ktor-web-app/common/src/commonMain/kotlin/Common.kt index d2b40503e..7618312a1 100644 --- a/samples/ktor-web-app/common/src/commonMain/kotlin/Common.kt +++ b/samples/ktor-web-app/common/src/commonMain/kotlin/Common.kt @@ -17,5 +17,5 @@ data class UserData( interface MyService : RemoteService { suspend fun hello(user: String, userData: UserData): String - suspend fun subscribeToNews(): Flow + fun subscribeToNews(): Flow } diff --git a/samples/ktor-web-app/frontend/build.gradle.kts b/samples/ktor-web-app/frontend/build.gradle.kts index 5a975aad2..65f5346a6 100644 --- a/samples/ktor-web-app/frontend/build.gradle.kts +++ b/samples/ktor-web-app/frontend/build.gradle.kts @@ -4,7 +4,7 @@ plugins { kotlin("multiplatform") - alias(libs.plugins.kotlin.plugin.serialization) + alias(libs.plugins.kotlinx.rpc) } kotlin { @@ -32,10 +32,11 @@ kotlin { implementation(libs.kotlinx.rpc.krpc.ktor.client) implementation(libs.kotlinx.rpc.krpc.serialization.json) - implementation(project.dependencies.platform(libs.kotlin.wrappers.bom)) + implementation(project.dependencies.enforcedPlatform(libs.kotlin.wrappers.bom)) implementation(libs.react) implementation(libs.react.dom) - implementation(libs.emotion) + implementation(libs.emotion.css) + implementation(libs.emotion.react) } } } diff --git a/samples/ktor-web-app/frontend/src/jsMain/kotlin/App.kt b/samples/ktor-web-app/frontend/src/jsMain/kotlin/App.kt index ce8284fa1..a81cc7440 100644 --- a/samples/ktor-web-app/frontend/src/jsMain/kotlin/App.kt +++ b/samples/ktor-web-app/frontend/src/jsMain/kotlin/App.kt @@ -44,7 +44,7 @@ val AppContainer = FC { props -> data?.also { welcomeData -> Welcome { this.data = welcomeData - this.service = service + this.news = service.subscribeToNews() } } ?: run { div { diff --git a/samples/ktor-web-app/frontend/src/jsMain/kotlin/Welcome.kt b/samples/ktor-web-app/frontend/src/jsMain/kotlin/Welcome.kt index d16c59e75..e35d22269 100644 --- a/samples/ktor-web-app/frontend/src/jsMain/kotlin/Welcome.kt +++ b/samples/ktor-web-app/frontend/src/jsMain/kotlin/Welcome.kt @@ -3,7 +3,7 @@ */ import emotion.react.css -import kotlinx.rpc.krpc.streamScoped +import kotlinx.coroutines.flow.Flow import react.FC import react.Props import react.dom.html.ReactHTML.div @@ -20,21 +20,19 @@ data class WelcomeData( ) external interface WelcomeProps : Props { - var service: MyService + var news: Flow var data: WelcomeData } -fun useArticles(service: MyService): List { +fun useArticles(news: Flow): List { var articles by useState(emptyList()) useEffectOnce { var localArticles = articles - streamScoped { - service.subscribeToNews().collect { - @Suppress("SuspiciousCollectionReassignment") - localArticles += it - articles = localArticles - } + news.collect { + @Suppress("SuspiciousCollectionReassignment") + localArticles += it + articles = localArticles } } @@ -42,7 +40,7 @@ fun useArticles(service: MyService): List { } val Welcome = FC { props -> - val articles = useArticles(props.service) + val articles = useArticles(props.news) div { css { diff --git a/samples/ktor-web-app/gradle/libs.versions.toml b/samples/ktor-web-app/gradle/libs.versions.toml index 989c5cd99..b9e74fd67 100644 --- a/samples/ktor-web-app/gradle/libs.versions.toml +++ b/samples/ktor-web-app/gradle/libs.versions.toml @@ -1,11 +1,11 @@ [versions] -kotlin = "2.1.10" -kotlin-wrappers-bom = "2025.1.2" -ktor = "3.0.3" -kotlinx-serialization-json = "1.8.0" -kotlinx-coroutines-core = "1.10.1" -logback = "1.5.16" -kotlinx-rpc = "0.5.1" +kotlin = "2.1.20" +kotlin-wrappers-bom = "2025.4.8" +ktor = "3.1.2" +kotlinx-serialization-json = "1.8.1" +kotlinx-coroutines-core = "1.10.2" +logback = "1.5.18" +kotlinx-rpc = "0.6.1" [libraries] # kotlin @@ -23,7 +23,8 @@ kotlinx-coroutines-core-jvm = { module = "org.jetbrains.kotlinx:kotlinx-coroutin kotlin-wrappers-bom = { module = "org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom", version.ref = "kotlin-wrappers-bom" } react = { module = "org.jetbrains.kotlin-wrappers:kotlin-react" } react-dom = { module = "org.jetbrains.kotlin-wrappers:kotlin-react-dom" } -emotion = { module = "org.jetbrains.kotlin-wrappers:kotlin-emotion" } +emotion-css = { module = "org.jetbrains.kotlin-wrappers:kotlin-emotion-css" } +emotion-react = { module = "org.jetbrains.kotlin-wrappers:kotlin-emotion-react" } # ktor ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" } diff --git a/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.jar b/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.jar index a4b76b953..9bbc975c7 100644 Binary files a/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.jar and b/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.properties b/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.properties index cea7a793a..37f853b1c 100644 --- a/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.properties +++ b/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/samples/ktor-web-app/gradlew b/samples/ktor-web-app/gradlew index f3b75f3b0..faf93008b 100755 --- a/samples/ktor-web-app/gradlew +++ b/samples/ktor-web-app/gradlew @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/samples/ktor-web-app/server/build.gradle.kts b/samples/ktor-web-app/server/build.gradle.kts index 5eac4363c..1f6a4f227 100644 --- a/samples/ktor-web-app/server/build.gradle.kts +++ b/samples/ktor-web-app/server/build.gradle.kts @@ -5,6 +5,7 @@ plugins { kotlin("jvm") alias(libs.plugins.kotlin.plugin.serialization) + alias(libs.plugins.kotlinx.rpc) application distribution } diff --git a/samples/ktor-web-app/server/src/main/kotlin/MyServiceImpl.kt b/samples/ktor-web-app/server/src/main/kotlin/MyServiceImpl.kt index 028f14abd..bfaeb660a 100644 --- a/samples/ktor-web-app/server/src/main/kotlin/MyServiceImpl.kt +++ b/samples/ktor-web-app/server/src/main/kotlin/MyServiceImpl.kt @@ -12,7 +12,7 @@ class MyServiceImpl(override val coroutineContext: CoroutineContext) : MyService return "Nice to meet you $user, how is it in ${userData.address}?" } - override suspend fun subscribeToNews(): Flow { + override fun subscribeToNews(): Flow { return flow { repeat(10) { delay(2000) diff --git a/samples/ktor-web-app/server/src/test/kotlin/ApplicationTest.kt b/samples/ktor-web-app/server/src/test/kotlin/ApplicationTest.kt index 837f60027..725623e90 100644 --- a/samples/ktor-web-app/server/src/test/kotlin/ApplicationTest.kt +++ b/samples/ktor-web-app/server/src/test/kotlin/ApplicationTest.kt @@ -35,11 +35,9 @@ class ApplicationTest { actual = service.hello("Alex", UserData("address1", "last")), ) - streamScoped { - assertEquals( - expected = List(10) { "Article number $it" }, - actual = service.subscribeToNews().toList(), - ) - } + assertEquals( + expected = List(10) { "Article number $it" }, + actual = service.subscribeToNews().toList(), + ) } } diff --git a/samples/simple-ktor-app/build.gradle.kts b/samples/simple-ktor-app/build.gradle.kts index b6bff4f7e..84ed8afc1 100644 --- a/samples/simple-ktor-app/build.gradle.kts +++ b/samples/simple-ktor-app/build.gradle.kts @@ -3,10 +3,10 @@ */ plugins { - kotlin("jvm") version "2.1.10" - kotlin("plugin.serialization") version "2.1.10" - id("io.ktor.plugin") version "3.0.3" - id("org.jetbrains.kotlinx.rpc.plugin") version "0.5.1" + kotlin("jvm") version "2.1.20" + kotlin("plugin.serialization") version "2.1.20" + id("io.ktor.plugin") version "3.1.2" + id("org.jetbrains.kotlinx.rpc.plugin") version "0.6.1" } group = "kotlinx.rpc.sample" @@ -28,17 +28,17 @@ kotlin { } dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.5.1") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.5.1") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.5.1") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.6.1") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.5.1") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.5.1") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.6.1") implementation("io.ktor:ktor-client-cio") implementation("io.ktor:ktor-server-netty-jvm") - implementation("ch.qos.logback:logback-classic:1.5.16") + implementation("ch.qos.logback:logback-classic:1.5.18") testImplementation("io.ktor:ktor-server-test-host") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.1.10") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.1.20") } diff --git a/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.jar b/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.jar index a4b76b953..9bbc975c7 100644 Binary files a/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.jar and b/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.properties b/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.properties index cea7a793a..37f853b1c 100644 --- a/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.properties +++ b/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/samples/simple-ktor-app/gradlew b/samples/simple-ktor-app/gradlew index f3b75f3b0..faf93008b 100755 --- a/samples/simple-ktor-app/gradlew +++ b/samples/simple-ktor-app/gradlew @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/samples/simple-ktor-app/src/main/kotlin/Client.kt b/samples/simple-ktor-app/src/main/kotlin/Client.kt index b00acb2e1..c62e588c6 100644 --- a/samples/simple-ktor-app/src/main/kotlin/Client.kt +++ b/samples/simple-ktor-app/src/main/kotlin/Client.kt @@ -12,7 +12,6 @@ import kotlinx.rpc.krpc.ktor.client.installKrpc import kotlinx.rpc.krpc.ktor.client.rpc import kotlinx.rpc.krpc.ktor.client.rpcConfig import kotlinx.rpc.krpc.serialization.json.json -import kotlinx.rpc.krpc.streamScoped import kotlinx.rpc.withService fun main() = runBlocking { @@ -37,10 +36,8 @@ fun main() = runBlocking { val recognizer: ImageRecognizer = client.withService() val stateJob = launch { - streamScoped { - recognizer.currentlyProcessedImage().collect { - println("New state, current image: $it") - } + recognizer.currentlyProcessedImage().collect { + println("New state, current image: $it") } } @@ -54,10 +51,8 @@ fun main() = runBlocking { } } - streamScoped { - val categories = recognizer.recognizeAll(imageFlow) - categories.collect { println("Recognized category: $it") } - } + val categories = recognizer.recognizeAll(imageFlow) + categories.collect { println("Recognized category: $it") } recognizer.cancel() ktorClient.close() diff --git a/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt b/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt index df7c4ea93..f2ccd0920 100644 --- a/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt +++ b/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt @@ -27,32 +27,32 @@ enum class Category { @Rpc interface ImageRecognizer : RemoteService { - suspend fun currentlyProcessedImage(): Flow + fun currentlyProcessedImage(): Flow suspend fun recognize(image: Image): Category - suspend fun recognizeAll(images: Flow): Flow + fun recognizeAll(images: Flow): Flow } class ImageRecognizerService(override val coroutineContext: CoroutineContext) : ImageRecognizer { - private val currentlyProcessedImage: MutableStateFlow = MutableStateFlow(null) + private val _currentlyProcessedImage: MutableStateFlow = MutableStateFlow(null) - override suspend fun currentlyProcessedImage(): Flow { + override fun currentlyProcessedImage(): Flow { return flow { - currentlyProcessedImage.collect { emit(it) } + _currentlyProcessedImage.collect { emit(it) } } } override suspend fun recognize(image: Image): Category { - currentlyProcessedImage.value = image + _currentlyProcessedImage.value = image val byte = image.data[0].toInt() delay(100) // heavy processing val result = if (byte == 0) Category.CAT else Category.DOG - currentlyProcessedImage.value = null + _currentlyProcessedImage.value = null return result } - override suspend fun recognizeAll(images: Flow): Flow { + override fun recognizeAll(images: Flow): Flow { return images.map { recognize(it) } } } diff --git a/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt b/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt index 3e93ffbf7..7c3482fed 100644 --- a/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt +++ b/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt @@ -42,14 +42,12 @@ class ApplicationTest { val flowList = mutableListOf() val job = CoroutineScope(Dispatchers.IO).launch { - streamScoped { - recognizer.currentlyProcessedImage().collect { image -> - val stringValue = image?.data?.toHexString() - flowList.add(stringValue) + recognizer.currentlyProcessedImage().collect { image -> + val stringValue = image?.data?.toHexString() + flowList.add(stringValue) - if (stringValue == "000203") { - coroutineContext.cancel() - } + if (stringValue == "000203") { + coroutineContext.cancel() } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index f99464f30..739a57188 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,7 +27,7 @@ plugins { id("conventions-repositories") id("conventions-version-resolution") id("conventions-develocity") - id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" } dependencyResolutionManagement { diff --git a/versions-root/kotlin-versions-lookup.csv b/versions-root/kotlin-versions-lookup.csv index bfadfbed1..981786a1e 100644 --- a/versions-root/kotlin-versions-lookup.csv +++ b/versions-root/kotlin-versions-lookup.csv @@ -1,7 +1,7 @@ Kotlin,atomicfu,serialization,detekt-gradle-plugin,gradle-kotlin-dsl,binary-compatibility-validator,kover -2.1.20,0.27.0,1.8.0,1.23.8,5.1.2,0.17.0,0.9.1 -2.1.10,0.27.0,1.8.0,1.23.8,5.1.2,0.17.0,0.9.1 -2.1.0,0.27.0,1.8.0,1.23.8,5.1.2,0.17.0,0.9.1 +2.1.20,0.27.0,1.8.1,1.23.8,5.1.2,0.17.0,0.9.1 +2.1.10,0.27.0,1.8.1,1.23.8,5.1.2,0.17.0,0.9.1 +2.1.0,0.27.0,1.8.1,1.23.8,5.1.2,0.17.0,0.9.1 2.0.21,0.26.0,1.7.3,1.23.8,5.1.2,0.16.3,0.8.3 2.0.20,0.26.0,1.7.3,1.23.8,5.1.2,0.16.3,0.8.3 2.0.10,0.26.0,1.7.1,1.23.8,5.1.2,0.16.3,0.8.3 diff --git a/versions-root/libs.versions.toml b/versions-root/libs.versions.toml index 3e388ecd1..fe30d0cf7 100644 --- a/versions-root/libs.versions.toml +++ b/versions-root/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # core library version -kotlinx-rpc = "0.6.0-SHAPSHOT" +kotlinx-rpc = "0.6.1" # kotlin kotlin-lang = "2.1.20" # or env.KOTLIN_VERSION @@ -8,19 +8,19 @@ kotlin-compiler = "0.0.0" # default to kotlin-lang or env.KOTLIN_COMPILER_VERSIO # kotlin independent versions detekt-analyzer = "1.23.6" -coroutines = "1.10.1" -ktor = "3.1.1" -kotlin-logging = "7.0.5" +coroutines = "1.10.2" +ktor = "3.1.2" +kotlin-logging = "7.0.6" slf4j = "2.0.17" logback = "1.3.14" gradle-plugin-publish = "1.3.1" -kotlin-wrappers = "2025.3.15" +kotlin-wrappers = "2025.4.8" junit4 = "4.13.2" junit5 = "5.12.1" intellij = "233.13135.128" gradle-doctor = "0.10.0" kotlinx-browser = "0.3" -shadow-jar = "9.0.0-beta10" +shadow-jar = "9.0.0-beta12" # Stub versions – relpaced based on kotlin, mostly for gradle-related (plugins) dependencies # but also for dependencies for compiler-specific modules. @@ -29,7 +29,7 @@ shadow-jar = "9.0.0-beta10" # # NOTE: When updating kotlin-versions-lookup.csv, update the latest version here for the Renovate configs atomicfu = "0.27.0" -serialization = "1.8.0" +serialization = "1.8.1" detekt-gradle-plugin = "1.23.8" gradle-kotlin-dsl = "5.1.2" binary-compatibility-validator = "0.17.0"