diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9cc8b7380..e933ca248 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,13 +14,13 @@ permissions: env: INSTANCE: 'kotlinx-rpc/rpc' ARTIFACT: 'webHelpRPC2-all.zip' - DOCKER_VERSION: '241.15989' + DOCKER_VERSION: '243.22562' ALGOLIA_ARTIFACT: 'algolia-indexes-RPC.zip' ALGOLIA_APP_NAME: 'MMA5Z3JT91' ALGOLIA_INDEX_NAME: 'prod_kotlin_rpc' ALGOLIA_KEY: '${{ secrets.ALGOLIA_KEY }}' CONFIG_JSON_PRODUCT: 'kotlinx-rpc' - CONFIG_JSON_VERSION: '0.4.0' + CONFIG_JSON_VERSION: '0.5.0' jobs: build: diff --git a/CHANGELOG.md b/CHANGELOG.md index 41779d6e8..5a1893d96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +# 0.5.0 +> Published 27 January 2024 + +### Features πŸŽ‰ +* Update Service Descriptors Generation by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/227 +* Kotlin 2.1.0 by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/244 +* Added basic CheckedTypeAnnotation impl with compiler plugin check by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/240 +* Strict mode by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/243 + +### Breaking Changes πŸ”΄ +* Api naming by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/236 +* Update Service Descriptors Generation by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/227 +* Added basic CheckedTypeAnnotation impl with compiler plugin check by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/240 + +### Deprecations ⚠️ +* Api naming by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/236 +* Strict mode by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/243 + +### Infra 🚧 +* Update the project structure to work with kotlin-master by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/234 +* Fixed version formatting with ENV vars by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/235 +* Fix Kotlin master compilation by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/245 +* Opt-out from annotations type safety analysis by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/246 + +### Other Changes 🧹 +* Added test for non-serializable params by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/237 +* Updated descriptor to use `RpcType` instead of `KType` directly by @Mr3zee in https://github.com/Kotlin/kotlinx-rpc/pull/239 + +**Full Changelog**: https://github.com/Kotlin/kotlinx-rpc/compare/0.4.0...0.5.0 + # 0.4.0 > Published 5 November 2024 diff --git a/README.md b/README.md index e046720bb..eafc64d71 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,16 @@ import kotlinx.rpc.annotations.Rpc @Rpc interface AwesomeService : RemoteService { suspend fun getNews(city: String): Flow + + suspend fun daysUntilStableRelese(): Int } ``` In your server code define how to respond by simply implementing the service: ```kotlin -class AwesomeServiceImpl(override val coroutineContext: CoroutineContext) : AwesomeService { +class AwesomeServiceImpl( + val parameters: AwesomeParameters, + override val coroutineContext: CoroutineContext, +) : AwesomeService { override suspend fun getNews(city: String): Flow { return flow { emit("Today is 23 degrees!") @@ -37,11 +42,19 @@ class AwesomeServiceImpl(override val coroutineContext: CoroutineContext) : Awes emit("New dogs cafe has opened doors to all fluffy customers!") } } + + override suspend fun daysUntilStableRelese(): Int { + retuen if (parameters.stable) 0 else { + parameters.daysUntilStable ?: error("Who says it will be stable?") + } + } } ``` Then, choose how do you want your service to communicate. For example, you can use integration with [Ktor](https://ktor.io/): ```kotlin +data class AwesomeParameters(val stable: Boolean, val daysUntilStable: Int?) + fun main() { embeddedServer(Netty, 8080) { install(Krpc) @@ -53,7 +66,9 @@ fun main() { } } - registerService { ctx -> AwesomeServiceImpl(ctx) } + registerService { ctx -> + AwesomeServiceImpl(AwesomeParameters(false, null), ctx) + } } } }.start(wait = true) @@ -71,8 +86,12 @@ val rpcClient = HttpClient { installKrpc() }.rpc { } } +val service = rpcClient.withService() + +service.daysUntilStableRelese() + streamScoped { - rpcClient.withService().getNews("KotlinBurg").collect { article -> + service.getNews("KotlinBurg").collect { article -> println(article) } } @@ -92,7 +111,7 @@ Example of a setup in a project's `build.gradle.kts`: plugins { kotlin("multiplatform") version "2.1.0" kotlin("plugin.serialization") version "2.1.0" - id("org.jetbrains.kotlinx.rpc.plugin") version "0.4.0" + id("org.jetbrains.kotlinx.rpc.plugin") version "0.5.0" } ``` @@ -107,15 +126,15 @@ And now you can add dependencies to your project: ```kotlin dependencies { // Client API - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.5.0") // Server API - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.5.0") // Serialization module. Also, protobuf and cbor are provided - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.5.0") // Transport implementation for Ktor - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.4.0") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.5.0") // Ktor API implementation("io.ktor:ktor-client-cio-jvm:$ktor_version") @@ -137,9 +156,18 @@ When using kRPC you only need to provide a transport or choose from the official Besides that, one can even provide their own protocol or integration with one to use with services and `kotlinx.rpc` API with it. Though possible, it is much more complicated way to use the library and generally not needed. -`kotlinx.rpc` aims to provide most common protocols integrations as well as the in-house one called kRPC. -Integrations in progress: -- Integration with [gRPC](https://grpc.io/) (in prototype) +`kotlinx.rpc` aims to provide support for the most common protocol integrations, as well as the in-house protocol called kRPC. + +## gRPC integration +The library provides experimental support for the [gRPC](https://grop.io) protocol. +The artifacts are published separately in our [Space repository](https://public.jetbrains.space/p/krpc/packages/maven/grpc). +Current latest version: + +![Dynamic XML Badge](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fpublic%2Fp%2Fkrpc%2Fgrpc%2Forg%2Fjetbrains%2Fkotlinx%2Fkotlinx-rpc-core%2Fmaven-metadata.xml&query=%2F%2Fmetadata%2Fversioning%2Flatest&label=Latest%20dev%20version&color=forest-green&cacheSeconds=60) + +For more information on gRPC usage, +see the [official documentation](https://kotlin.github.io/kotlinx-rpc/grpc-configuration.html). +For a working example, see the [sample gRPC project](/samples/grpc-app). ## Kotlin compatibility We support all stable Kotlin versions starting from 2.0.0: diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt index 0ae4f2974..a8bae872f 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen @@ -12,6 +12,10 @@ object FirRpcPredicates { annotated(RpcClassId.rpcAnnotation.asSingleFqName()) // @Rpc } + internal val rpcMeta = DeclarationPredicate.create { + metaAnnotated(RpcClassId.rpcAnnotation.asSingleFqName(), includeItself = true) + } + internal val checkedAnnotationMeta = DeclarationPredicate.create { metaAnnotated(RpcClassId.checkedTypeAnnotation.asSingleFqName(), includeItself = false) } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt index a7e053662..4221420b2 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen.checkers @@ -9,7 +9,9 @@ import kotlinx.rpc.codegen.FirRpcPredicates import kotlinx.rpc.codegen.checkers.diagnostics.FirRpcDiagnostics import kotlinx.rpc.codegen.isRemoteService import kotlinx.rpc.codegen.remoteServiceSupertypeSource +import kotlinx.rpc.codegen.rpcAnnotation import kotlinx.rpc.codegen.rpcAnnotationSource +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind @@ -18,6 +20,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChe import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.utils.isInterface import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.types.resolvedType class FirRpcAnnotationChecker(private val ctx: FirCheckersContext) : FirRegularClassChecker(MppCheckerKind.Common) { override fun check( @@ -26,12 +29,15 @@ class FirRpcAnnotationChecker(private val ctx: FirCheckersContext) : FirRegularC reporter: DiagnosticReporter, ) { val rpcAnnotated = context.session.predicateBasedProvider.matches(FirRpcPredicates.rpc, declaration) + val rpcMetaAnnotated = context.session.predicateBasedProvider.matches(FirRpcPredicates.rpcMeta, declaration) - if (!declaration.isInterface && rpcAnnotated) { + if (!declaration.isInterface && declaration.classKind != ClassKind.ANNOTATION_CLASS && rpcMetaAnnotated) { reporter.reportOn( source = declaration.symbol.rpcAnnotationSource(context.session), factory = FirRpcDiagnostics.WRONG_RPC_ANNOTATION_TARGET, context = context, + a = declaration.symbol.rpcAnnotation(context.session)?.resolvedType + ?: error("Unexpected unresolved annotation type for declaration: ${declaration.symbol.classId.asSingleFqName()}"), ) } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt index dc9701e85..ecb6e253b 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen.checkers.diagnostics @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry object FirRpcDiagnostics { val MISSING_RPC_ANNOTATION by error0() val MISSING_SERIALIZATION_MODULE by warning0() - val WRONG_RPC_ANNOTATION_TARGET by error0() + val WRONG_RPC_ANNOTATION_TARGET by error1() val CHECKED_ANNOTATION_VIOLATION by error1() val NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE by error0() val AD_HOC_POLYMORPHISM_IN_RPC_SERVICE by error2() diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt index 4c6425001..20ca86bed 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen.checkers.diagnostics @@ -29,7 +29,8 @@ object RpcDiagnosticRendererFactory : BaseDiagnosticRendererFactory() { put( factory = FirRpcDiagnostics.WRONG_RPC_ANNOTATION_TARGET, - message = "@Rpc annotation is only applicable to interfaces.", + message = "@{0} annotation is only applicable to interfaces and annotation classes.", + rendererA = FirDiagnosticRenderers.RENDER_TYPE, ) put( diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/RpcCall.kt b/core/src/commonMain/kotlin/kotlinx/rpc/RpcCall.kt index 7c08860ba..6c123fd34 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/RpcCall.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/RpcCall.kt @@ -1,11 +1,14 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc import kotlinx.rpc.descriptor.RpcServiceDescriptor +@Deprecated("Use RpcCall instead", ReplaceWith("RpcCall"), level = DeprecationLevel.ERROR) +public typealias RPCCall = RpcCall + /** * Represents a method or field call of an RPC service. * diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/RpcClient.kt b/core/src/commonMain/kotlin/kotlinx/rpc/RpcClient.kt index 84ee9e252..019d66418 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/RpcClient.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/RpcClient.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc @@ -8,6 +8,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlin.coroutines.CoroutineContext +@Deprecated("Use RpcClient instead", ReplaceWith("RpcClient"), level = DeprecationLevel.ERROR) +public typealias RPCClient = RpcClient + /** * [RpcClient] represents an abstraction of an RPC client, that can handle requests from several RPC services, * transform them, send to the server and handle responses and errors. diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/RpcEagerField.kt b/core/src/commonMain/kotlin/kotlinx/rpc/RpcEagerField.kt index 4d3e313fa..9f68337c7 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/RpcEagerField.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/RpcEagerField.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc @@ -8,6 +8,10 @@ package kotlinx.rpc * The field marked with this annotation will be initialized with the service creation. */ @Target(AnnotationTarget.PROPERTY) +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public annotation class RpcEagerField @Deprecated("Use RpcEagerField instead", ReplaceWith("RpcEagerField"), level = DeprecationLevel.ERROR) diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/UninitializedRpcFieldException.kt b/core/src/commonMain/kotlin/kotlinx/rpc/UninitializedRpcFieldException.kt index f27cffd49..927c1cc48 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/UninitializedRpcFieldException.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/UninitializedRpcFieldException.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc @@ -18,6 +18,10 @@ public typealias UninitializedRPCFieldException = UninitializedRpcFieldException * * Use [awaitFieldInitialization] to await for the field initialization */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public class UninitializedRpcFieldException(serviceName: String, property: KProperty<*>) : Exception() { override val message: String = "${property.name} field of RPC service \"$serviceName\" in not initialized" } diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt b/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt index 9491d0cec..3a93aef0d 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc @@ -27,6 +27,10 @@ import kotlin.reflect.KClass * @param getter function that returns the field of the context service to wait for. * @return service filed after it was initialized. */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public suspend fun <@Rpc T : Any, R> T.awaitFieldInitialization(getter: T.() -> R): R { val field = getter() @@ -56,6 +60,10 @@ public suspend fun <@Rpc T : Any, R> T.awaitFieldInitialization(getter: T.() -> * @param T service type * @return specified service, after all of it's field were initialized. */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public suspend inline fun <@Rpc reified T : Any> T.awaitFieldInitialization(): T { return awaitFieldInitialization(T::class) } @@ -79,6 +87,10 @@ public suspend inline fun <@Rpc reified T : Any> T.awaitFieldInitialization(): T * @param kClass [KClass] of the [T] type. * @return specified service, after all of it's field were initialized. */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public suspend fun <@Rpc T : Any> T.awaitFieldInitialization(kClass: KClass): T { serviceDescriptorOf(kClass) .getFields(this) diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt b/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt index 288f17481..c361a4921 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.descriptor @@ -44,6 +44,10 @@ public interface RpcServiceDescriptor<@Rpc T : Any> { public val fqName: String @InternalRpcApi + @Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) public fun getFields(service: T): List> public fun getCallable(name: String): RpcCallable? @@ -68,6 +72,10 @@ public sealed interface RpcInvokator<@Rpc T : Any> { } @ExperimentalRpcApi + @Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) public fun interface Field<@Rpc T : Any> : RpcInvokator { public fun call(service: T): Any? } diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/registerField.kt b/core/src/commonMain/kotlin/kotlinx/rpc/registerField.kt index 59ad6cdcc..74f5b3987 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/registerField.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/registerField.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc @@ -25,6 +25,10 @@ import kotlinx.rpc.internal.RpcFlow * @param serviceId id of the service, that made the call * @return Flow instance to be consumed. */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public fun RpcClient.registerPlainFlowField( serviceScope: CoroutineScope, descriptor: RpcServiceDescriptor<*>, @@ -46,6 +50,10 @@ public fun RpcClient.registerPlainFlowField( * @param serviceId id of the service, that made the call * @return SharedFlow instance to be consumed. */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public fun RpcClient.registerSharedFlowField( serviceScope: CoroutineScope, descriptor: RpcServiceDescriptor<*>, @@ -67,6 +75,10 @@ public fun RpcClient.registerSharedFlowField( * @param serviceId id of the service, that made the call * @return StateFlow instance to be consumed. */ +@Deprecated( + "Fields are deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, +) public fun RpcClient.registerStateFlowField( serviceScope: CoroutineScope, descriptor: RpcServiceDescriptor<*>, diff --git a/docs/pages/kotlinx-rpc/.idea/kotlinx-rpc.iml b/docs/pages/kotlinx-rpc/.idea/kotlinx-rpc.iml new file mode 100644 index 000000000..610219404 --- /dev/null +++ b/docs/pages/kotlinx-rpc/.idea/kotlinx-rpc.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/pages/kotlinx-rpc/.idea/modules.xml b/docs/pages/kotlinx-rpc/.idea/modules.xml new file mode 100644 index 000000000..6a8e3f943 --- /dev/null +++ b/docs/pages/kotlinx-rpc/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/pages/kotlinx-rpc/help-versions.json b/docs/pages/kotlinx-rpc/help-versions.json index a5e4e23e9..445d8a961 100644 --- a/docs/pages/kotlinx-rpc/help-versions.json +++ b/docs/pages/kotlinx-rpc/help-versions.json @@ -1,3 +1,3 @@ [ - {"version":"0.4.0","url":"/kotlinx-rpc/0.4.0/","isCurrent":true} + {"version":"0.5.0","url":"/kotlinx-rpc/0.5.0/","isCurrent":true} ] diff --git a/docs/pages/kotlinx-rpc/rpc.tree b/docs/pages/kotlinx-rpc/rpc.tree index 53f20105a..e671d020f 100644 --- a/docs/pages/kotlinx-rpc/rpc.tree +++ b/docs/pages/kotlinx-rpc/rpc.tree @@ -14,8 +14,10 @@ + + @@ -23,9 +25,17 @@ + + + + + + + + diff --git a/docs/pages/kotlinx-rpc/topics/0-5-0.topic b/docs/pages/kotlinx-rpc/topics/0-5-0.topic new file mode 100644 index 000000000..0e480eb37 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/0-5-0.topic @@ -0,0 +1,167 @@ + + + + +

+ Version 0.5.0 introduces breaking changes. +

+ + +

+ This release introduces annotation type-safety. + As a result, some code that previously compiled successfully may now produce errors: +

+ + @Rpc + interface MyService : RemoteService + + class MyServiceImpl : MyService + + fun <@Rpc T : Any> withService() {} + + + + withService<MyService>() // ok + withService<MyServiceImpl>() // ok in 0.4.0, error in 0.5.0 + +

+ This should not cause any major issues, as the affected code was already incorrect + in previous releases and would have failed at runtime in 0.4.0. +

+
+ + +

+ This release introduces Strict Mode. + Some service declarations are now deprecated with a warning. + In upcoming releases, these warnings will be replaced with errors. +

+

+ Deprecated service APIs: +

+ +
  • StateFlow and SharedFlow usage
  • +
  • Fields in services
  • +
  • Nested Flows
  • +
  • Not top-level server side flows
  • +
    +

    + For more information, see the Strict Mode documentation. +

    +
    + + + The following APIs were renamed to satisfy Kotlin style guides and bring even more consistency to the library: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OldNew
    kotlinx.rpc.RPCClientkotlinx.rpc.RpcClient
    kotlinx.rpc.RPCServerkotlinx.rpc.RpcServer
    kotlinx.rpc.UninitializedRPCFieldExceptionkotlinx.rpc.UninitializedRpcFieldException
    kotlinx.rpc.RPCEagerFieldkotlinx.rpc.RpcEagerField
    kotlinx.rpc.RPCEagerFieldkotlinx.rpc.RpcEagerField
    kotlinx.rpc.internal.utils.InternalRPCApikotlinx.rpc.internal.utils.InternalRpcApi
    kotlinx.rpc.internal.utils.ExperimentalRPCApikotlinx.rpc.internal.utils.ExperimentalRpcApi
    kotlinx.rpc.krpc.RPCConfigkotlinx.rpc.krpc.KrpcConfig
    kotlinx.rpc.krpc.RPCConfigBuilderkotlinx.rpc.krpc.KrpcConfigBuilder
    kotlinx.rpc.krpc.RPCTransportkotlinx.rpc.krpc.KrpcTransport
    kotlinx.rpc.krpc.RPCTransportMessagekotlinx.rpc.krpc.KrpcTransportMessage
    kotlinx.rpc.krpc.client.KRPCClientkotlinx.rpc.krpc.client.KrpcClient
    kotlinx.rpc.krpc.server.KRPCServerkotlinx.rpc.krpc.server.KrpcServer
    kotlinx.rpc.krpc.ktor.client.installRPCkotlinx.rpc.krpc.ktor.client.installKrpc
    kotlinx.rpc.krpc.ktor.client.RPCkotlinx.rpc.krpc.ktor.client.Krpc
    kotlinx.rpc.krpc.ktor.client.KtorRPCClientkotlinx.rpc.krpc.ktor.client.KtorRpcClient
    kotlinx.rpc.krpc.ktor.server.RPCkotlinx.rpc.krpc.ktor.server.Krpc
    kotlinx.rpc.krpc.ktor.server.RPCRoutekotlinx.rpc.krpc.ktor.server.KrpcRoute
    kotlinx.rpc.krpc.serialization.RPCSerialFormatkotlinx.rpc.krpc.serialization.KrpcSerialFormat
    kotlinx.rpc.krpc.serialization.RPCSerialFormatBuilderkotlinx.rpc.krpc.serialization.KrpcSerialFormatBuilder
    kotlinx.rpc.krpc.serialization.RPCSerialFormatConfigurationkotlinx.rpc.krpc.serialization.KrpcSerialFormatConfiguration
    kotlinx.rpc.krpc.serialization.cbor.RPCCborSerialFormatkotlinx.rpc.krpc.serialization.cbor.KrpcCborSerialFormat
    kotlinx.rpc.krpc.serialization.json.RPCJsonSerialFormatkotlinx.rpc.krpc.serialization.json.KrpcJsonSerialFormat
    kotlinx.rpc.krpc.serialization.protobuf.RPCProtobufSerialFormatkotlinx.rpc.krpc.serialization.protobuf.KrpcProtobufSerialFormat
    +
    + + +

    + New API is introduced to access compiler generated code - . +

    +
    +
    diff --git a/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic b/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic new file mode 100644 index 000000000..135146a7b --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic @@ -0,0 +1,73 @@ + + + + +

    + The library introduces a concept of annotation type-safety. Consider the following example: +

    + + @Rpc + interface MyService : RemoteService + + class MyServiceImpl : MyService + + fun <T : RemoteService> withService() {} + +

    + The compiler can't guarantee that the passed type parameter is the one for which the code generation was run: +

    + + withService<MyService>() // ok + withService<MyServiceImpl>() // compile time ok, runtime throws + +

    + The compiler plugin enforces annotation type-safety by requiring type parameters to have specific annotations, + like @Rpc. +

    + + @Rpc + interface MyService : RemoteService + + class MyServiceImpl : MyService + + fun <@Rpc T : Any> withService() {} + + + + withService<MyService>() // ok + withService<MyServiceImpl>() // compile time error + + + + Annotation type safety only ensures that the resolved type parameters are annotated with the required + annotation. + The actual type of the passed argument may differ: + + fun <@Rpc T : Any> registerService(body: () -> T) {} + + // T is resolved to MyService, + // but 'body' returns MyServiceImpl + registerService<MyService> { ctx -> MyServiceImpl(ctx) } + + // Error: T is resolved to MyServiceImpl + registerService { ctx -> MyServiceImpl(ctx) } + + + + + This feature is highly experimental and may lead to unexpected behaviour. If you encounter any issues, + please
    report them. + + To prevent critical bugs from affecting your app, you can disable this feature using the following + configuration: + + // build.gradle.kts + rpc { + annotationTypeSafetyEnabled = false // true by default + } + + + diff --git a/docs/pages/kotlinx-rpc/topics/configuration.topic b/docs/pages/kotlinx-rpc/topics/configuration.topic index 49d7864df..0b382bd18 100644 --- a/docs/pages/kotlinx-rpc/topics/configuration.topic +++ b/docs/pages/kotlinx-rpc/topics/configuration.topic @@ -53,6 +53,11 @@ sharedFlowParameters DSL + + These parameters are deprecated since 0.5.0. For more information, + see the migration guide. + + rpcClientConfig { sharedFlowParameters { diff --git a/docs/pages/kotlinx-rpc/topics/features.topic b/docs/pages/kotlinx-rpc/topics/features.topic index 707d88795..54b5830ac 100644 --- a/docs/pages/kotlinx-rpc/topics/features.topic +++ b/docs/pages/kotlinx-rpc/topics/features.topic @@ -8,182 +8,103 @@ - -

    You can send and receive Kotlin Flows in RPC - methods. - That includes Flow, SharedFlow, and StateFlow, but - NOT - their mutable versions. - Flows can be nested and can be included in other classes, like this: -

    - - - @Serializable - data class StreamResult { - @Contextual - val innerFlow: StateFlow<Int> - } - - @Rpc - interface MyService : RemoteService { - suspend fun sendStream(stream: Flow<Flow<Int>>): Flow<StreamResult> - } - -

    Note that flows that are declared in classes (like in StreamResult) require - Contextual - annotation.

    -

    To use flows in your code - you need to use special streamScoped function - that will provide your flows with their lifetime:

    - - - @Rpc - interface MyService : RemoteService { - suspend fun sendFlow(flow: Flow<Int>) - } - - val myService = rpcClient.withService<MyService>() - - streamScoped { - val flow = flow { - repeat(10) { i -> - emit(i) - } - } - - myService.sendFlow(flow) - } - -

    In that case all your flows, including incoming and outgoing, - will work until the streamScoped function completes. - After that, all streams that are still live will be closed.

    -

    You can have multiple RPC call and flows inside the streamScoped function, even from - different services.

    -

    On server side, you can use invokeOnStreamScopeCompletion handler inside your methods - to execute code after streamScoped on client side has closed. - It might be useful to clean resources, for example:

    - - - override suspend fun hotFlow(): StateFlow<Int> { - val state = MutableStateFlow(-1) + +

    + You can send and receive Kotlin Flows in RPC + methods. + However, this only applies to the Flow type. StateFlow and SharedFlow + are not supported, and there are no plans to add support for them. +

    - incomingHotFlowJob = launch { - repeat(Int.MAX_VALUE) { value -> - state.value = value + + @Serializable + data class StreamRequest( + @Contextual + val innerFlow: Flow<Int> + ) - hotFlowMirror.first { it == value } - } - } + @Rpc + interface MyService : RemoteService { + suspend fun sendStream(stream: Flow<Int>): Flow<String> - invokeOnStreamScopeCompletion { - incomingHotFlowJob.cancel() - } + suspend fun streamRequest(request: StreamRequest) + } + - return state - } -
    -

    Note that this API is experimental and may be removed in future releases.

    -

    - Another way of managing streams is to do it manually. - For this, you can use the StreamScope constructor function together with - withStreamScope: -

    - - val streamScope = StreamScope(myJob) - withStreamScope(streamScope) { - // use streams here - } - -
    - -

    Our protocol provides you with an ability to declare service fields:

    +

    + Another requirement is that server-side steaming (flows that are returned from a function), + must be the top-level type: +

    - - @Rpc - interface MyService : RemoteService { - val plainFlow: Flow<Int> - val sharedFlow: SharedFlow<Int> - val stateFlow: StateFlow<Int> - } + + @Serializable + data class StreamResult( + @Contextual + val innerFlow: Flow<Int> + ) - // ### Server code ### + @Rpc + interface MyService : RemoteService { + suspend fun serverStream(): Flow<String> // ok + suspend fun serverStream(): StreamResult // not ok + } + - class MyServiceImpl(override val coroutineContext: CoroutineContext) : MyService { - override val plainFlow: Flow<Int> = flow { - emit(1) - } + + Note that flows that are declared in classes (like in StreamResult) require a + Contextual + annotation. + - override val sharedFlow: SharedFlow<Int> = MutableSharedFlow(replay = 1) - override val stateFlow: StateFlow<Int> = MutableStateFlow(value = 1) - } - -

    Field declarations are only supported for these three types: Flow, - SharedFlow and StateFlow.

    -

    You don't need to use streamScoped function to work with streams in fields.

    -

    To learn more about the limitations of such declarations, - see Field declarations in services.

    -
    - -

    Fields are supported in the in-house RPC protocol, - but the support comes with its limitations. - There always will be a considerable time gap between the - initial access to a field and the moment information about this field arrives from a server. - This makes it hard to provide good uniform API for all possible field types, - so now will limit supported types to Flow, SharedFlow and - StateFlow - (excluding mutable versions of them). - To work with these fields, you may use additional provided APIs:

    -

    Firstly, we define two possible states of a flow field: - uninitialized - and - initialized - . - Before the first information about this flow has arrived from a server, - the flow is in - uninitialized - state. - In this state, if you access any of its - fields - (replayCache for SharedFlow and StateFlow, and value - for StateFlow) - you will get a UninitializedRpcFieldException. - If you call a suspend collect method on them, - execution will suspend until the state is - initialized - and then the actual collect method will be executed. - The same ability to suspend execution until the state is - initialized - can be achieved by using awaitFieldInitialization function: +

    + To use flows in your code, use the streamScoped function + that will provide your flows with their lifetime:

    @Rpc interface MyService : RemoteService { - val flow: StateFlow<Int> + suspend fun sendFlow(flow: Flow<Int>) } - // ### Somewhere in client code ### - val myService: MyService = rpcClient.withService<MyService>() + val myService = rpcClient.withService<MyService>() - val value = myService.flow.value // throws UninitializedRpcFieldException - val value = myService.awaitFieldInitialization { flow }.value // OK - // or - val value = myService.awaitFieldInitialization().flow.value // OK - // or - val firstFive = myService.flow.take(5).toList() // OK - -

    Secondly, we provide you with an instrument to make initialization faster. - By default, all fields are lazy. - By adding @RpcEagerField annotation, you can change this behavior, - so that fields will be initialized when the service in created - (when withService method is called):

    + streamScoped { + val flow = flow { + repeat(10) { i -> + emit(i) + } + } + myService.sendFlow(flow) + } +
    +

    + In that case all your flows, including incoming and outgoing, + will work until the streamScoped function completes. + After that, all streams that are still live will be closed. +

    +

    + You can have multiple RPC calls and flows inside the streamScoped function, including those from + different services. +

    +

    + On the server side, you can use the invokeOnStreamScopeCompletion handler inside your methods + to execute code after streamScoped on the client side has closed. + It might be useful to clean resources, for example. +

    + + Note that this API is experimental and may be removed in future releases. + +

    + Another way of managing streams is to do it manually. + For this, you can use the StreamScope constructor function together with + withStreamScope: +

    - @Rpc - interface MyService : RemoteService { - val lazyFlow: Flow<Int> // initialized on first access - - @RpcEagerField - val eagerFlow: Flow<Int> // initialized on service creation + val streamScope = StreamScope(myJob) + withStreamScope(streamScope) { + // use streams here } diff --git a/docs/pages/kotlinx-rpc/topics/grpc-client.topic b/docs/pages/kotlinx-rpc/topics/grpc-client.topic new file mode 100644 index 000000000..58cdc6676 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/grpc-client.topic @@ -0,0 +1,31 @@ + + + + +

    + Example of using a gRPC client: +

    + + val grpcClient = GrpcClient("localhost", 8080) { + usePlaintext() + } + + val recognizer = grpcClient.withService<ImageRecognizer>() + + val image = Image { + data = byteArrayOf(0, 1, 2, 3) + } + val result = recognizer.recognize(image) + println("Recognized category: ${result.category}") + + grpcClient.cancel() + + +
  • + usePlaintext() - is a JVM binding to Java gRPC runtime. Other bindings are also present. +
  • +
    +
    \ No newline at end of file diff --git a/docs/pages/kotlinx-rpc/topics/grpc-configuration.topic b/docs/pages/kotlinx-rpc/topics/grpc-configuration.topic new file mode 100644 index 000000000..5ac509320 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/grpc-configuration.topic @@ -0,0 +1,130 @@ + + + + + +

    + Artifacts for gRPC integration are published separately + and updated frequently, independent of the main releases. +

    +

    + + Latest dev version + +

    +
    + +

    + gRPC integration is available in an experimental state. + The artifacts are published separately in our Space repository. +

    + +

    Below is an example of a project setup.

    + settings.gradle.kts: + + pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/krpc/grpc") + } + } + +

    + build.gradle.kts: +

    + + plugins { + kotlin("jvm") version "2.1.0" + kotlin("plugin.serialization") version "2.1.0" + id("org.jetbrains.kotlinx.rpc.plugin") version "<version>" + id("com.google.protobuf") version "0.9.4" + } + + repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/krpc/grpc") + } + + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-rpc-grpc-core:<version>") + implementation("ch.qos.logback:logback-classic:1.5.16") + implementation("io.grpc:grpc-netty:1.69.0") + } + +

    Here <version> comes from the badge above.

    + + The setup has only been tested on Kotlin/JVM projects. + +
    + +

    + gRPC requires additional code generation from the protoc + compiler. + This can be setup up in the following way: +

    + + protobuf { + protoc { + artifact = "com.google.protobuf:protoc:4.29.3" + } + + plugins { + create("kotlinx-rpc") { + artifact = "org.jetbrains.kotlinx:kotlinx-rpc-protobuf-plugin:<version>:all@jar" + } + + create("grpc") { + artifact = "io.grpc:protoc-gen-grpc-java:1.69.0" + } + + create("grpckt") { + artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar" + } + } + + generateProtoTasks { + all().all { + plugins { + create("kotlinx-rpc") { + option("debugOutput=protobuf-plugin.log") + option("messageMode=interface") + } + create("grpc") + create("grpckt") + } + } + } + } + + +
  • + Four source sets will be generated: + +
  • java - protobuf Java declarations
  • +
  • grpc - gRPC Java declarations
  • +
  • grpckt - gRPC Kotlin wrappers for Java
  • +
  • kotlinx-rpc - pur wrappers for all of the above
  • +
    +

    + You won't need to use the first three directly, only the declarations from the kotlinx-rpc + source set are intended to be used. +

    + Source sets are generated into $BUILD_DIR/generated/source/proto/main directory + unless specified otherwise. + +
  • + option("debugOutput=protobuf-plugin.log") lets you specify the file + for the protoc plugin debug output. +
  • +
  • + option("messageMode=interface") is intended to be like so. Don't change it. +
  • + +
    +
    diff --git a/docs/pages/kotlinx-rpc/topics/grpc-server.topic b/docs/pages/kotlinx-rpc/topics/grpc-server.topic new file mode 100644 index 000000000..25b8916b3 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/grpc-server.topic @@ -0,0 +1,29 @@ + + + + +

    + Example of using a gRPC server: +

    + + val grpcServer = GrpcServer(8080) { + registerService<ImageRecognizer> { ImageRecognizerImpl() } + } + + grpcServer.start() + grpcServer.awaitTermination() + + +
  • + GrpcServer allows to register multiple services, like regular RpcServer. + However, CoroutineContext parameter is not needed and should not be used. +
  • +
  • + GrpcServer does not bind to Java gRPC Server, + but provides some functions to cover for that. +
  • +
    +
    diff --git a/docs/pages/kotlinx-rpc/topics/grpc-services.topic b/docs/pages/kotlinx-rpc/topics/grpc-services.topic new file mode 100644 index 000000000..469df5338 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/grpc-services.topic @@ -0,0 +1,104 @@ + + + + +

    + To define a service, create a new .proto file in the proto folder next to your source sets: +

    + + β”œβ”€β”€ build.gradle.kts + β”œβ”€β”€ settings.gradle.kts + └── src + β”œβ”€β”€ main + β”‚ β”œβ”€β”€ kotlin + β”‚ β”‚ β”œβ”€β”€ Client.kt + β”‚ β”‚ β”œβ”€β”€ ImageRecognizer.kt + β”‚ β”‚ └── Server.kt + β”‚ └── resources + β”‚ └── logback.xml + └── proto + └── image-recognizer.proto + + +

    + Inside the .proto file define your service: +

    + + syntax = "proto3"; + + message Image { + bytes data = 1; + } + + message RecogniseResult { + int32 category = 1; + } + + service ImageRecognizer { + rpc recognize(Image) returns (RecogniseResult); + } + +

    + This will generate the necessary code, including the most important interfaces: + ImageRecognizer, Image, RecogniseResult: +

    + + @Grpc + interface ImageRecognizer { + suspend fun recognize(image: Image): RecogniseResult + } + + interface RecogniseResult { + val category: Int + + companion object + } + + interface Image { + val data: ByteArray + + companion object + } + +

    + You can implement the ImageRecognizer: +

    + + class ImageRecognizerImpl : ImageRecognizer { + override suspend fun recognize(image: Image): RecogniseResult { + val byte = image.data[0].toInt() + delay(100) // heavy processing + val result = RecogniseResult { + category = if (byte == 0) 0 else 1 + } + return result + } + } + +

    + Here you can also see the usage of the RecogniseResult interface. + To create an instance, use its .invoke() extension function: +

    + + RecogniseResult { + category = 0 + } + + + +

    Current known limitations:

    + +
  • No streaming
  • +
  • Only primitive types in messages
  • +
  • Mandatory java and kotlin protoc generation in addition to our codegen
  • +
  • Kotlin/JVM project only
  • +
    +

    + If you encounter other unexpected limitations or bugs, + please report them +

    +
    +
    diff --git a/docs/pages/kotlinx-rpc/topics/service-descriptors.topic b/docs/pages/kotlinx-rpc/topics/service-descriptors.topic new file mode 100644 index 000000000..421c1b9c8 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/service-descriptors.topic @@ -0,0 +1,21 @@ + + + + + + This API is experimental and may be changed at any time. + +

    + Service Descriptors allow you to access entities generated by the compiler plugin. + You can access them by using the following code: +

    + + serviceDescriptorOf<MyService>() + +

    + For the list of available entities, refer to the source code. +

    +
    diff --git a/docs/pages/kotlinx-rpc/topics/services.topic b/docs/pages/kotlinx-rpc/topics/services.topic index b10a1dee0..0cfa5ecb2 100644 --- a/docs/pages/kotlinx-rpc/topics/services.topic +++ b/docs/pages/kotlinx-rpc/topics/services.topic @@ -41,7 +41,9 @@

    Now we can implement the service, so server knows how to process incoming requests.

    - class MyServiceImpl(override val coroutineContext: CoroutineContext) : MyService { + class MyServiceImpl( + override val coroutineContext: CoroutineContext, + ) : MyService { override suspend fun hello(name: String): String { return "Hello, $name! I'm server!" } diff --git a/docs/pages/kotlinx-rpc/topics/strict-mode.topic b/docs/pages/kotlinx-rpc/topics/strict-mode.topic new file mode 100644 index 000000000..18e8c46a1 --- /dev/null +++ b/docs/pages/kotlinx-rpc/topics/strict-mode.topic @@ -0,0 +1,96 @@ + + + + +

    + Starting with version 0.5.0, the library introduces major changes to the service APIs. + The following declarations will be gradually restricted: +

    + +
  • + StateFlow and SharedFlow +

    Deprecation level: WARNING

    + + @Rpc + interface Service : RemoteService { + suspend fun old(): StateFlow<Int> // deprecated + + suspend fun new(): Flow<Int> // use .stateIn on the client side + } + +
  • +
  • + Fields +

    Deprecation level: WARNING

    + + @Rpc + interface Service : RemoteService { + val old: Flow<Int> // deprecated + + suspend fun new(): Flow<Int> // store flow locally + } + +
  • +
  • + Nested Flows +

    Deprecation level: WARNING

    + + @Rpc + interface Service : RemoteService { + suspend fun old(): Flow<Flow<Int>> // deprecated + + // no particular alternative, depends on the use case + } + +
  • +
  • + Not top-level server flows +

    Deprecation level: WARNING

    + + + data class SpotifyWrapped(val myMusicFlow: Flow<Rap>, val extra: Data) + + @Rpc + interface Service : RemoteService { + suspend fun old(): SpotifyWrapped // deprecated + + // one should consider message delivery order when calling these + suspend fun new(): Flow<Rap> + suspend fun getData(): Data + } + +
  • +
    + +

    + Deprecation levels are controlled by the Gradle rpc extension: +

    + + // build.gradle.kts + plugins { + id("org.jetbrains.kotlinx.rpc.plugin") + } + + rpc { + strict { + stateFlow = RpcStrictMode.WARNING + sharedFlow = RpcStrictMode.WARNING + nestedFlow = RpcStrictMode.WARNING + notTopLevelServerFlow = RpcStrictMode.WARNING + fields = RpcStrictMode.WARNING + } + } + +

    + Modes RpcStrictMode.NONE and RpcStrictMode.ERROR are available. +

    + + + Note that setting RpcStrictMode.NONE should not be done permanently. + All deprecated APIs will become errors in future without an option to suppress it. + Consider your migration path in advance. + +
    diff --git a/docs/pages/kotlinx-rpc/v.list b/docs/pages/kotlinx-rpc/v.list index 21b24f30b..873005992 100644 --- a/docs/pages/kotlinx-rpc/v.list +++ b/docs/pages/kotlinx-rpc/v.list @@ -14,6 +14,6 @@ - + diff --git a/docs/pages/kotlinx-rpc/writerside.cfg b/docs/pages/kotlinx-rpc/writerside.cfg index 0a0f6d429..14e4f4b76 100644 --- a/docs/pages/kotlinx-rpc/writerside.cfg +++ b/docs/pages/kotlinx-rpc/writerside.cfg @@ -12,5 +12,5 @@ - + diff --git a/gradle-conventions-settings/src/main/kotlin/conventions-version-resolution.settings.gradle.kts b/gradle-conventions-settings/src/main/kotlin/conventions-version-resolution.settings.gradle.kts index 71932571a..65511c09a 100644 --- a/gradle-conventions-settings/src/main/kotlin/conventions-version-resolution.settings.gradle.kts +++ b/gradle-conventions-settings/src/main/kotlin/conventions-version-resolution.settings.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import java.io.BufferedReader @@ -178,12 +178,16 @@ fun VersionCatalogBuilder.resolveLibraryVersion(versionCatalog: Map "" - it == "SNAPSHOT" -> "-$it" - else -> "-eap-$it" + else -> "-$it" } } ?: "" - version(SettingsConventions.LIBRARY_CORE_VERSION_ALIAS, libraryCoreVersion + eapVersion) + val resultingVersion = when (eapVersion) { + "" -> libraryCoreVersion + else -> libraryCoreVersion.substringBefore('-') + eapVersion + } + + version(SettingsConventions.LIBRARY_CORE_VERSION_ALIAS, resultingVersion) } fun String.kotlinVersionParsed(): KotlinVersion { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d6c49620e..6a70dc6d8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,9 +1,9 @@ # -# Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# 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\://packages.jetbrains.team/files/p/krpc/build-deps/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://packages.jetbrains.team/files/p/krpc/build-deps/distributions/gradle-8.12-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index fe1eead6e..03189d92f 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -2458,12 +2458,7 @@ wrappy@1: resolved "https://packages.jetbrains.team/npm/p/krpc/build-deps/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@8.5.0: - version "8.5.0" - resolved "https://packages.jetbrains.team/npm/p/krpc/build-deps/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -ws@^8.18.0: +ws@8.18.0, ws@^8.18.0: version "8.18.0" resolved "https://packages.jetbrains.team/npm/p/krpc/build-deps/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== diff --git a/krpc/krpc-client/build.gradle.kts b/krpc/krpc-client/build.gradle.kts index 39f18e9a9..2f338be67 100644 --- a/krpc/krpc-client/build.gradle.kts +++ b/krpc/krpc-client/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import util.applyAtomicfuPlugin @@ -18,6 +18,11 @@ kotlin { dependencies { api(projects.krpc.krpcCore) + // KRPC-137 Remove temporary explicit dependencies in 2.1.10 and unmute compiler tests + implementation(projects.core) + implementation(projects.utils) + implementation(projects.krpc.krpcSerialization.krpcSerializationCore) + implementation(libs.serialization.core) implementation(libs.kotlin.reflect) diff --git a/krpc/krpc-core/build.gradle.kts b/krpc/krpc-core/build.gradle.kts index 40f24dfd8..84055d510 100644 --- a/krpc/krpc-core/build.gradle.kts +++ b/krpc/krpc-core/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import util.applyAtomicfuPlugin @@ -20,6 +20,9 @@ kotlin { api(projects.krpc.krpcSerialization.krpcSerializationCore) implementation(projects.krpc.krpcLogging) + // KRPC-137 Remove temporary explicit dependencies in 2.1.10 and unmute compiler tests + implementation(projects.utils) + api(libs.coroutines.core) implementation(libs.serialization.core) implementation(libs.kotlin.reflect) diff --git a/krpc/krpc-core/src/commonMain/kotlin/kotlinx/rpc/krpc/KrpcConfig.kt b/krpc/krpc-core/src/commonMain/kotlin/kotlinx/rpc/krpc/KrpcConfig.kt index 76c349243..962e965e1 100644 --- a/krpc/krpc-core/src/commonMain/kotlin/kotlinx/rpc/krpc/KrpcConfig.kt +++ b/krpc/krpc-core/src/commonMain/kotlin/kotlinx/rpc/krpc/KrpcConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.krpc @@ -27,11 +27,19 @@ public sealed class KrpcConfigBuilder private constructor() { * parameters, and thus they cannot be encoded and transferred. * So then creating their instance on an endpoint, the library should know which parameters to use. */ + @Deprecated( + "SharedFlow support is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) @Suppress("MemberVisibilityCanBePrivate") public class SharedFlowParametersBuilder internal constructor() { /** * The number of values replayed to new subscribers (cannot be negative, defaults to zero). */ + @Deprecated( + "SharedFlow support is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) public var replay: Int = DEFAULT_REPLAY /** @@ -39,6 +47,10 @@ public sealed class KrpcConfigBuilder private constructor() { * emit does not suspend while there is a buffer space remaining * (optional, cannot be negative, defaults to zero). */ + @Deprecated( + "SharedFlow support is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) public var extraBufferCapacity: Int = DEFAULT_EXTRA_BUFFER_CAPACITY /** @@ -50,10 +62,15 @@ public sealed class KrpcConfigBuilder private constructor() { * In the absence of subscribers only the most recent replay values are stored * and the buffer overflow behavior is never triggered and has no effect. */ + @Deprecated( + "SharedFlow support is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) public var onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND @InternalRpcApi public fun builder(): () -> MutableSharedFlow = { + @Suppress("DEPRECATION") MutableSharedFlow(replay, extraBufferCapacity, onBufferOverflow) } @@ -69,12 +86,18 @@ public sealed class KrpcConfigBuilder private constructor() { } } + @Suppress("DEPRECATION") protected var sharedFlowBuilder: () -> MutableSharedFlow = SharedFlowParametersBuilder().builder() /** * @see SharedFlowParametersBuilder */ - public fun sharedFlowParameters(builder: SharedFlowParametersBuilder.() -> Unit) { + @Deprecated( + "SharedFlow support is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-5-0.html", + level = DeprecationLevel.WARNING, + ) + public fun sharedFlowParameters(builder: @Suppress("DEPRECATION") SharedFlowParametersBuilder.() -> Unit) { + @Suppress("DEPRECATION") sharedFlowBuilder = SharedFlowParametersBuilder().apply(builder).builder() } diff --git a/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts b/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts index b3d2a7af1..c7aaef9a7 100644 --- a/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts +++ b/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ plugins { @@ -14,6 +14,10 @@ kotlin { api(projects.krpc.krpcClient) api(projects.krpc.krpcKtor.krpcKtorCore) + // KRPC-137 Remove temporary explicit dependencies in 2.1.10 and unmute compiler tests + implementation(projects.krpc.krpcCore) + implementation(projects.core) + api(libs.ktor.client.core) api(libs.ktor.client.websockets) diff --git a/krpc/krpc-ktor/krpc-ktor-core/build.gradle.kts b/krpc/krpc-ktor/krpc-ktor-core/build.gradle.kts index ff3a78cc6..b3c8081c5 100644 --- a/krpc/krpc-ktor/krpc-ktor-core/build.gradle.kts +++ b/krpc/krpc-ktor/krpc-ktor-core/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ plugins { @@ -14,6 +14,10 @@ kotlin { dependencies { api(projects.krpc.krpcCore) + // KRPC-137 Remove temporary explicit dependencies in 2.1.10 and unmute compiler tests + implementation(projects.core) + implementation(projects.utils) + implementation(libs.ktor.websockets) implementation(libs.coroutines.core) implementation(libs.serialization.core) diff --git a/krpc/krpc-server/build.gradle.kts b/krpc/krpc-server/build.gradle.kts index 2413cb4c3..facbe73ce 100644 --- a/krpc/krpc-server/build.gradle.kts +++ b/krpc/krpc-server/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import util.applyAtomicfuPlugin @@ -18,6 +18,11 @@ kotlin { dependencies { api(projects.krpc.krpcCore) + // KRPC-137 Remove temporary explicit dependencies in 2.1.10 and unmute compiler tests + implementation(projects.core) + implementation(projects.utils) + implementation(projects.krpc.krpcSerialization.krpcSerializationCore) + implementation(projects.krpc.krpcLogging) implementation(libs.serialization.core) diff --git a/samples/grpc-app/.gitignore b/samples/grpc-app/.gitignore new file mode 100644 index 000000000..c426c32f8 --- /dev/null +++ b/samples/grpc-app/.gitignore @@ -0,0 +1,36 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ \ No newline at end of file diff --git a/samples/grpc-app/build.gradle.kts b/samples/grpc-app/build.gradle.kts new file mode 100644 index 000000000..b0ec82e38 --- /dev/null +++ b/samples/grpc-app/build.gradle.kts @@ -0,0 +1,63 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +plugins { + kotlin("jvm") version "2.1.0" + kotlin("plugin.serialization") version "2.1.0" + id("org.jetbrains.kotlinx.rpc.plugin") version "0.5.0-grpc-6" + id("com.google.protobuf") version "0.9.4" +} + +group = "kotlinx.rpc.sample" +version = "0.0.1" + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/krpc/grpc") +} + +kotlin { + jvmToolchain(11) +} + +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-rpc-grpc-core:0.5.0-grpc-6") + implementation("ch.qos.logback:logback-classic:1.5.16") + implementation("io.grpc:grpc-netty:1.69.0") +} + +val buildDirPath: String = project.layout.buildDirectory.get().asFile.absolutePath + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:4.29.3" + } + + plugins { + create("kotlinx-rpc") { + artifact = "org.jetbrains.kotlinx:kotlinx-rpc-protobuf-plugin:0.5.0-grpc-6:all@jar" + } + + create("grpc") { + artifact = "io.grpc:protoc-gen-grpc-java:1.69.0" + } + + create("grpckt") { + artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar" + } + } + + generateProtoTasks { + all().all { + plugins { + create("kotlinx-rpc") { + option("debugOutput=$buildDirPath/protobuf-plugin.log") + option("messageMode=interface") + } + create("grpc") + create("grpckt") + } + } + } +} diff --git a/samples/grpc-app/gradle.properties b/samples/grpc-app/gradle.properties new file mode 100644 index 000000000..eac9698fd --- /dev/null +++ b/samples/grpc-app/gradle.properties @@ -0,0 +1,5 @@ +# +# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# + +kotlin.code.style=official diff --git a/samples/grpc-app/gradle/wrapper/gradle-wrapper.jar b/samples/grpc-app/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..a4b76b953 Binary files /dev/null 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 new file mode 100644 index 000000000..b2f467f62 --- /dev/null +++ b/samples/grpc-app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,11 @@ +# +# 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 +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/samples/grpc-app/gradlew b/samples/grpc-app/gradlew new file mode 100755 index 000000000..89f1e740c --- /dev/null +++ b/samples/grpc-app/gradlew @@ -0,0 +1,237 @@ +#!/bin/sh + +# +# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions Β«$varΒ», Β«${var}Β», Β«${var:-default}Β», Β«${var+SET}Β», +# Β«${var#prefix}Β», Β«${var%suffix}Β», and Β«$( cmd )Β»; +# * compound commands having a testable exit status, especially Β«caseΒ»; +# * various built-in commands including Β«commandΒ», Β«setΒ», and Β«ulimitΒ». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_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. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/samples/grpc-app/gradlew.bat b/samples/grpc-app/gradlew.bat new file mode 100644 index 000000000..9d21a2183 --- /dev/null +++ b/samples/grpc-app/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/grpc-app/settings.gradle.kts b/samples/grpc-app/settings.gradle.kts new file mode 100644 index 000000000..4feb7105d --- /dev/null +++ b/samples/grpc-app/settings.gradle.kts @@ -0,0 +1,13 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +rootProject.name = "grpc-app" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/krpc/grpc") + } +} diff --git a/samples/grpc-app/src/main/kotlin/Client.kt b/samples/grpc-app/src/main/kotlin/Client.kt new file mode 100644 index 000000000..4a81eae34 --- /dev/null +++ b/samples/grpc-app/src/main/kotlin/Client.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import kotlinx.coroutines.cancel +import kotlinx.coroutines.runBlocking +import kotlinx.rpc.grpc.GrpcClient +import kotlinx.rpc.withService + +fun main(): Unit = runBlocking { + val grpcClient = GrpcClient("localhost", 8080) { + usePlaintext() + } + + val recognizer = grpcClient.withService() + + val image = Image { + data = byteArrayOf(0, 1, 2, 3) + } + val result = recognizer.recognize(image) + println("Recognized category: ${result.category}") + + grpcClient.cancel() +} diff --git a/samples/grpc-app/src/main/kotlin/ImageRecognizer.kt b/samples/grpc-app/src/main/kotlin/ImageRecognizer.kt new file mode 100644 index 000000000..2608539c3 --- /dev/null +++ b/samples/grpc-app/src/main/kotlin/ImageRecognizer.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import kotlinx.coroutines.delay + +class ImageRecognizerImpl : ImageRecognizer { + override suspend fun recognize(image: Image): RecogniseResult { + val byte = image.data[0].toInt() + delay(100) // heavy processing + val result = RecogniseResult { + category = if (byte == 0) 0 else 1 + } + return result + } +} diff --git a/samples/grpc-app/src/main/kotlin/Server.kt b/samples/grpc-app/src/main/kotlin/Server.kt new file mode 100644 index 000000000..681586c0b --- /dev/null +++ b/samples/grpc-app/src/main/kotlin/Server.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import kotlinx.coroutines.runBlocking +import kotlinx.rpc.grpc.GrpcServer +import kotlinx.rpc.registerService + +fun main(): Unit = runBlocking { + val grpcServer = GrpcServer(8080) { + registerService { ImageRecognizerImpl() } + } + + grpcServer.start() + grpcServer.awaitTermination() +} diff --git a/samples/grpc-app/src/main/proto/image-recognizer.proto b/samples/grpc-app/src/main/proto/image-recognizer.proto new file mode 100644 index 000000000..38dde9d68 --- /dev/null +++ b/samples/grpc-app/src/main/proto/image-recognizer.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +message Image { + bytes data = 1; +} + +message RecogniseResult { + int32 category = 1; +} + +service ImageRecognizer { + rpc recognize(Image) returns (RecogniseResult); +} diff --git a/samples/grpc-app/src/main/resources/logback.xml b/samples/grpc-app/src/main/resources/logback.xml new file mode 100644 index 000000000..efd0ec942 --- /dev/null +++ b/samples/grpc-app/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/samples/ktor-all-platforms-app/.fleet/settings.json b/samples/ktor-all-platforms-app/.fleet/settings.json new file mode 100644 index 000000000..05bf50272 --- /dev/null +++ b/samples/ktor-all-platforms-app/.fleet/settings.json @@ -0,0 +1,3 @@ +{ + "gradle.autoImportOnSave": false +} \ No newline at end of file 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 ef6420d6c..6f63073b4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import androidx.compose.animation.AnimatedVisibility @@ -14,7 +14,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import io.ktor.client.* import io.ktor.http.* -import kotlinx.rpc.krpc.ktor.client.installRPC +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 @@ -28,7 +28,7 @@ expect val DEV_SERVER_HOST: String val client by lazy { HttpClient { - installRPC() + installKrpc() } } diff --git a/samples/ktor-all-platforms-app/gradle/libs.versions.toml b/samples/ktor-all-platforms-app/gradle/libs.versions.toml index 3043bce74..7cf70fad0 100644 --- a/samples/ktor-all-platforms-app/gradle/libs.versions.toml +++ b/samples/ktor-all-platforms-app/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "2.0.21" +kotlin = "2.1.0" -agp = "8.7.2" +agp = "8.8.0-alpha05" android-compileSdk = "35" android-minSdk = "24" android-targetSdk = "35" @@ -12,14 +12,14 @@ androidx-core-ktx = "1.15.0" androidx-espresso-core = "3.6.1" androidx-material = "1.12.0" androidx-test-junit = "1.2.1" -compose = "1.7.5" -compose-plugin = "1.7.0" +compose = "1.7.6" +compose-plugin = "1.8.0-alpha01" junit = "4.13.2" -ktor = "3.0.1" -logback = "1.5.12" -serialization = "1.7.3" -coroutines = "1.9.0" -kotlinx-rpc = "0.4.0" +ktor = "3.0.3" +logback = "1.5.16" +serialization = "1.8.0" +coroutines = "1.10.1" +kotlinx-rpc = "0.5.0" [libraries] # kotlin 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 df97d72b8..cea7a793a 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.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-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 f5feea6d6..f3b75f3b0 100755 --- a/samples/ktor-all-platforms-app/gradlew +++ b/samples/ktor-all-platforms-app/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt b/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt index 9fb57c627..2e8d0f649 100644 --- a/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt +++ b/samples/ktor-all-platforms-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.sample @@ -10,7 +10,7 @@ import io.ktor.server.application.* import io.ktor.server.netty.* import io.ktor.server.plugins.cors.routing.* import io.ktor.server.routing.* -import kotlinx.rpc.krpc.ktor.server.RPC +import kotlinx.rpc.krpc.ktor.server.Krpc import kotlinx.rpc.krpc.ktor.server.rpc import kotlinx.rpc.krpc.serialization.json.json @@ -18,7 +18,7 @@ fun main(args: Array): Unit = EngineMain.main(args) @Suppress("unused") fun Application.module() { - install(RPC) + install(Krpc) installCORS() 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 7755a8551..80440fd8f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.sample @@ -8,7 +8,7 @@ import UserData import UserService import io.ktor.server.testing.* import kotlinx.coroutines.flow.toList -import kotlinx.rpc.krpc.ktor.client.installRPC +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 @@ -21,7 +21,7 @@ class ApplicationTest { @Test fun testRoot() = testApplication { val service = createClient { - installRPC() + installKrpc() }.rpc("/api") { rpcConfig { serialization { diff --git a/samples/ktor-all-platforms-app/settings.gradle.kts b/samples/ktor-all-platforms-app/settings.gradle.kts index 124e4e40d..a05d83951 100644 --- a/samples/ktor-all-platforms-app/settings.gradle.kts +++ b/samples/ktor-all-platforms-app/settings.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("UnstableApiUsage") diff --git a/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/data/Client.kt b/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/data/Client.kt index e6aeded96..01a658e50 100644 --- a/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/data/Client.kt +++ b/samples/ktor-android-app/app/src/main/kotlin/kotlinx/rpc/sample/data/Client.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.sample.data @@ -7,15 +7,15 @@ package kotlinx.rpc.sample.data import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.request.url -import kotlinx.rpc.RPCClient -import kotlinx.rpc.krpc.ktor.client.installRPC +import kotlinx.rpc.RpcClient +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 -suspend fun createRpcClient(): RPCClient { +suspend fun createRpcClient(): RpcClient { return HttpClient(OkHttp) { - installRPC() + installKrpc() }.rpc { url("ws://10.0.2.2:8080/api") 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 9eb7679d5..43ee0fc08 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.sample.ui @@ -14,14 +14,14 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import kotlinx.rpc.RPCClient +import kotlinx.rpc.RpcClient import kotlinx.rpc.krpc.streamScoped import kotlinx.rpc.withService import kotlinx.rpc.sample.MyService import kotlinx.rpc.sample.UserData class AppViewModel : ViewModel() { - private var rpcClient: RPCClient? = null + private var rpcClient: RpcClient? = null private var apiService: MyService? = null private val _uiState = MutableStateFlow(null) @@ -64,4 +64,4 @@ class AppViewModel : ViewModel() { } } } -} \ No newline at end of file +} diff --git a/samples/ktor-android-app/gradle/libs.versions.toml b/samples/ktor-android-app/gradle/libs.versions.toml index 37383d37c..f59b98e5e 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.7.2" -kotlin = "2.0.21" +agp = "8.8.0" +kotlin = "2.1.0" androidx-activityCompose = "1.9.3" androidx-appcompat = "1.7.0" androidx-constraintlayout = "2.2.0" androidx-core-ktx = "1.15.0" androidx-test-junit = "1.2.1" -compose = "1.7.5" +compose = "1.7.6" compose-plugin = "1.5.14" # https://mvnrepository.com/artifact/androidx.compose.compiler/compiler -compose-bom = "2024.10.01" +compose-bom = "2024.12.01" material3 = "1.3.1" junit = "4.13.2" -ktor = "3.0.1" -kotlinx-serialization-json = "1.7.3" -kotlinx-coroutines-core = "1.9.0" -logback = "1.5.12" -kotlinx-rpc = "0.4.0" +ktor = "3.0.3" +kotlinx-serialization-json = "1.8.0" +kotlinx-coroutines-core = "1.10.1" +logback = "1.5.16" +kotlinx-rpc = "0.5.0" [libraries] # kotlin diff --git a/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.properties b/samples/ktor-android-app/gradle/wrapper/gradle-wrapper.properties index df97d72b8..cea7a793a 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.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-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 f5feea6d6..f3b75f3b0 100755 --- a/samples/ktor-android-app/gradlew +++ b/samples/ktor-android-app/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt b/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt index d7a9370a4..1ac0a6929 100644 --- a/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt +++ b/samples/ktor-android-app/server/src/main/kotlin/kotlinx/rpc/sample/Application.kt @@ -1,12 +1,12 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.sample import io.ktor.server.application.* import io.ktor.server.cio.* import io.ktor.server.routing.* -import io.ktor.server.websocket.* +import kotlinx.rpc.krpc.ktor.server.Krpc import kotlinx.rpc.krpc.ktor.server.rpc import kotlinx.rpc.krpc.serialization.json.json @@ -14,7 +14,7 @@ fun main(args: Array): Unit = EngineMain.main(args) @Suppress("unused") fun Application.module() { - install(WebSockets) + install(Krpc) routing { rpc("/api") { 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 1c76842c1..a706b3b6b 100644 --- a/samples/ktor-android-app/server/src/test/kotlin/ApplicationTest.kt +++ b/samples/ktor-android-app/server/src/test/kotlin/ApplicationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import io.ktor.client.request.* @@ -9,7 +9,7 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.testing.* import kotlinx.coroutines.flow.toList -import kotlinx.rpc.krpc.ktor.client.installRPC +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 @@ -24,7 +24,7 @@ class ApplicationTest { @Test fun testRoot() = testApplication { val service = createClient { - installRPC() + installKrpc() }.rpc("/api") { rpcConfig { serialization { diff --git a/samples/ktor-web-app/README.md b/samples/ktor-web-app/README.md index fe9243f8e..e19bf290e 100644 --- a/samples/ktor-web-app/README.md +++ b/samples/ktor-web-app/README.md @@ -1,9 +1,6 @@ # Ktor Web App Sample application with Kotlin/Js on frontend and Kotlin/Jvm on backend,that uses kRPC with Ktor to communicate. -> Warning: the code is broken due to https://youtrack.jetbrains.com/issue/KT-71757/ -> No workarounds for now - ### Running frontend To run frontend in development mode, run this command: ```bash diff --git a/samples/ktor-web-app/frontend/build.gradle.kts b/samples/ktor-web-app/frontend/build.gradle.kts index 51a5e440f..5a975aad2 100644 --- a/samples/ktor-web-app/frontend/build.gradle.kts +++ b/samples/ktor-web-app/frontend/build.gradle.kts @@ -28,6 +28,7 @@ kotlin { implementation(libs.kotlin.stdlib.js) implementation(libs.ktor.client.js) implementation(libs.ktor.client.websockets.js) + implementation(libs.kotlinx.rpc.core) implementation(libs.kotlinx.rpc.krpc.ktor.client) implementation(libs.kotlinx.rpc.krpc.serialization.json) 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 bec029654..ce8284fa1 100644 --- a/samples/ktor-web-app/frontend/src/jsMain/kotlin/App.kt +++ b/samples/ktor-web-app/frontend/src/jsMain/kotlin/App.kt @@ -2,7 +2,7 @@ * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import kotlinx.rpc.RPCClient +import kotlinx.rpc.RpcClient import kotlinx.rpc.withService import react.FC import react.Props @@ -11,7 +11,7 @@ import react.useEffectOnce import react.useState val App = FC { - var rpcClient by useState(null) + var rpcClient by useState(null) useEffectOnce { rpcClient = initRpcClient() diff --git a/samples/ktor-web-app/frontend/src/jsMain/kotlin/RPC.kt b/samples/ktor-web-app/frontend/src/jsMain/kotlin/RPC.kt index 3205500f8..1330b72d2 100644 --- a/samples/ktor-web-app/frontend/src/jsMain/kotlin/RPC.kt +++ b/samples/ktor-web-app/frontend/src/jsMain/kotlin/RPC.kt @@ -5,15 +5,15 @@ import io.ktor.client.* import io.ktor.client.engine.js.* import io.ktor.client.request.* -import kotlinx.rpc.RPCClient -import kotlinx.rpc.krpc.ktor.client.installRPC +import kotlinx.rpc.RpcClient +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 -suspend fun initRpcClient(): RPCClient { +suspend fun initRpcClient(): RpcClient { return HttpClient(Js) { - installRPC() + installKrpc() }.rpc { url("ws://localhost:8080/api") diff --git a/samples/ktor-web-app/gradle/libs.versions.toml b/samples/ktor-web-app/gradle/libs.versions.toml index 7f245a080..a15bf2a42 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.0.21" -kotlin-wrappers-bom = "1.0.0-pre.823" -ktor = "3.0.1" -kotlinx-serialization-json = "1.7.3" -kotlinx-coroutines-core = "1.9.0" -logback = "1.5.12" -kotlinx-rpc = "0.4.0" +kotlin = "2.1.0" +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.0" [libraries] # kotlin diff --git a/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.properties b/samples/ktor-web-app/gradle/wrapper/gradle-wrapper.properties index df97d72b8..cea7a793a 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.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-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 f5feea6d6..f3b75f3b0 100755 --- a/samples/ktor-web-app/gradlew +++ b/samples/ktor-web-app/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/samples/ktor-web-app/server/src/main/kotlin/Application.kt b/samples/ktor-web-app/server/src/main/kotlin/Application.kt index 7b859168d..13b1580cf 100644 --- a/samples/ktor-web-app/server/src/main/kotlin/Application.kt +++ b/samples/ktor-web-app/server/src/main/kotlin/Application.kt @@ -8,7 +8,7 @@ import io.ktor.server.cio.* import io.ktor.server.http.content.* import io.ktor.server.plugins.cors.routing.* import io.ktor.server.routing.* -import kotlinx.rpc.krpc.ktor.server.RPC +import kotlinx.rpc.krpc.ktor.server.Krpc import kotlinx.rpc.krpc.ktor.server.rpc import kotlinx.rpc.krpc.serialization.json.json @@ -16,7 +16,7 @@ fun main(args: Array): Unit = EngineMain.main(args) @Suppress("unused") fun Application.module() { - install(RPC) + install(Krpc) installCORS() 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 a11982f4d..837f60027 100644 --- a/samples/ktor-web-app/server/src/test/kotlin/ApplicationTest.kt +++ b/samples/ktor-web-app/server/src/test/kotlin/ApplicationTest.kt @@ -1,10 +1,10 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import io.ktor.server.testing.* import kotlinx.coroutines.flow.toList -import kotlinx.rpc.krpc.ktor.client.installRPC +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 @@ -21,7 +21,7 @@ class ApplicationTest { } val service = createClient { - installRPC() + installKrpc() }.rpc("/api") { rpcConfig { serialization { diff --git a/samples/simple-ktor-app/build.gradle.kts b/samples/simple-ktor-app/build.gradle.kts index d0c86a58c..00cff85e5 100644 --- a/samples/simple-ktor-app/build.gradle.kts +++ b/samples/simple-ktor-app/build.gradle.kts @@ -1,12 +1,12 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ plugins { - kotlin("jvm") version "2.0.21" - kotlin("plugin.serialization") version "2.0.21" - id("io.ktor.plugin") version "3.0.1" - id("org.jetbrains.kotlinx.rpc.plugin") version "0.4.0" + kotlin("jvm") version "2.1.0" + kotlin("plugin.serialization") version "2.1.0" + id("io.ktor.plugin") version "3.0.3" + id("org.jetbrains.kotlinx.rpc.plugin") version "0.5.0" } group = "kotlinx.rpc.sample" @@ -28,17 +28,17 @@ kotlin { } dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.4.0") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.4.0") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.5.0") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.4.0") - implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.5.0") implementation("io.ktor:ktor-client-cio") implementation("io.ktor:ktor-server-netty-jvm") - implementation("ch.qos.logback:logback-classic:1.5.12") + implementation("ch.qos.logback:logback-classic:1.5.16") testImplementation("io.ktor:ktor-server-test-host") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.0.10") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.1.0") } diff --git a/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.properties b/samples/simple-ktor-app/gradle/wrapper/gradle-wrapper.properties index df97d72b8..cea7a793a 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.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-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 f5feea6d6..f3b75f3b0 100755 --- a/samples/simple-ktor-app/gradlew +++ b/samples/simple-ktor-app/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/samples/simple-ktor-app/src/main/kotlin/Client.kt b/samples/simple-ktor-app/src/main/kotlin/Client.kt index 284847855..b00acb2e1 100644 --- a/samples/simple-ktor-app/src/main/kotlin/Client.kt +++ b/samples/simple-ktor-app/src/main/kotlin/Client.kt @@ -3,12 +3,12 @@ */ import io.ktor.client.* -import io.ktor.client.plugins.websocket.* import io.ktor.http.* import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +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 @@ -17,7 +17,7 @@ import kotlinx.rpc.withService fun main() = runBlocking { val ktorClient = HttpClient { - install(WebSockets) + installKrpc() } val client = ktorClient.rpc { @@ -37,8 +37,10 @@ fun main() = runBlocking { val recognizer: ImageRecognizer = client.withService() val stateJob = launch { - recognizer.currentlyProcessedImage.collect { - println("New state, current image: $it") + streamScoped { + recognizer.currentlyProcessedImage().collect { + println("New state, current image: $it") + } } } diff --git a/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt b/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt index 57a3df5e0..df7c4ea93 100644 --- a/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt +++ b/samples/simple-ktor-app/src/main/kotlin/ImageRecognizer.kt @@ -5,10 +5,9 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.rpc.RemoteService -import kotlinx.rpc.RPCEagerField import kotlinx.rpc.annotations.Rpc import kotlinx.serialization.Serializable import kotlin.coroutines.CoroutineContext @@ -28,8 +27,7 @@ enum class Category { @Rpc interface ImageRecognizer : RemoteService { - @RPCEagerField - val currentlyProcessedImage: StateFlow + suspend fun currentlyProcessedImage(): Flow suspend fun recognize(image: Image): Category @@ -37,7 +35,13 @@ interface ImageRecognizer : RemoteService { } class ImageRecognizerService(override val coroutineContext: CoroutineContext) : ImageRecognizer { - override val currentlyProcessedImage: MutableStateFlow = MutableStateFlow(null) + private val currentlyProcessedImage: MutableStateFlow = MutableStateFlow(null) + + override suspend fun currentlyProcessedImage(): Flow { + return flow { + currentlyProcessedImage.collect { emit(it) } + } + } override suspend fun recognize(image: Image): Category { currentlyProcessedImage.value = image diff --git a/samples/simple-ktor-app/src/main/kotlin/Server.kt b/samples/simple-ktor-app/src/main/kotlin/Server.kt index c5fa1ae91..b8fb73b34 100644 --- a/samples/simple-ktor-app/src/main/kotlin/Server.kt +++ b/samples/simple-ktor-app/src/main/kotlin/Server.kt @@ -6,7 +6,7 @@ import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.routing.* -import kotlinx.rpc.krpc.ktor.server.RPC +import kotlinx.rpc.krpc.ktor.server.Krpc import kotlinx.rpc.krpc.ktor.server.rpc import kotlinx.rpc.krpc.serialization.json.json @@ -17,7 +17,7 @@ fun main() { } fun Application.module() { - install(RPC) + install(Krpc) routing { rpc("/image-recognizer") { diff --git a/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt b/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt index 2b754f0a8..3e93ffbf7 100644 --- a/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt +++ b/samples/simple-ktor-app/src/test/kotlin/ApplicationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ import io.ktor.server.testing.* @@ -8,10 +8,12 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.rpc.awaitFieldInitialization +import kotlinx.rpc.krpc.ktor.client.installKrpc import kotlinx.rpc.krpc.ktor.client.installRPC 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 org.junit.Test import kotlin.test.assertContentEquals @@ -26,7 +28,7 @@ class ApplicationTest { } val rpcClient = createClient { - installRPC() + installKrpc() }.rpc("/image-recognizer") { rpcConfig { serialization { @@ -39,15 +41,15 @@ class ApplicationTest { val flowList = mutableListOf() - assertEquals(null, recognizer.awaitFieldInitialization { currentlyProcessedImage }.value) - val job = CoroutineScope(Dispatchers.IO).launch { - recognizer.currentlyProcessedImage.collect { image -> - val stringValue = image?.data?.toHexString() - flowList.add(stringValue) + streamScoped { + recognizer.currentlyProcessedImage().collect { image -> + val stringValue = image?.data?.toHexString() + flowList.add(stringValue) - if (stringValue == "000203") { - coroutineContext.cancel() + if (stringValue == "000203") { + coroutineContext.cancel() + } } } } @@ -57,6 +59,6 @@ class ApplicationTest { job.join() - assertContentEquals(listOf(null, "010203", null, "000203"), flowList) + assertContentEquals(listOf("010203", null, "000203"), flowList) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 584358ef4..63f37299e 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.8.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } dependencyResolutionManagement { diff --git a/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/BoxTestGenerated.java b/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/BoxTestGenerated.java index 03f61a0be..ea1dbe626 100644 --- a/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/BoxTestGenerated.java +++ b/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/BoxTestGenerated.java @@ -1,7 +1,7 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen.test.runners; @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.Ignore; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.File; @@ -19,6 +21,7 @@ @SuppressWarnings("all") @TestMetadata("src/testData/box") @TestDataPath("$PROJECT_ROOT") +@Disabled("KRPC-137") public class BoxTestGenerated extends AbstractBoxTest { @Test public void testAllFilesPresentInBox() { diff --git a/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java b/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java index 995639c62..94cdb9319 100644 --- a/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java +++ b/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java @@ -1,7 +1,7 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen.test.runners; @@ -9,8 +9,8 @@ import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; - import java.io.File; import java.util.regex.Pattern; @@ -18,6 +18,7 @@ @SuppressWarnings("all") @TestMetadata("src/testData/diagnostics") @TestDataPath("$PROJECT_ROOT") +@Disabled("KRPC-137") public class DiagnosticTestGenerated extends AbstractDiagnosticTest { @Test public void testAllFilesPresentInDiagnostics() { diff --git a/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt b/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt index 12eb91624..7aba04105 100644 --- a/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt +++ b/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt @@ -1,23 +1,22 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.rpc.codegen.test -import kotlinx.rpc.codegen.test.runners.AbstractBoxTest -import kotlinx.rpc.codegen.test.runners.AbstractDiagnosticTest import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5 fun main() { generateTestGroupSuiteWithJUnit5 { testGroup(testDataRoot = "src/testData", testsRoot = "src/test-gen") { - testClass { - model("diagnostics") - } + // KRPC-137 Remove temporary explicit dependencies in 2.1.10 and unmute compiler tests +// testClass { +// model("diagnostics") +// } - testClass { - model("box") - } +// testClass { +// model("box") +// } } } } diff --git a/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt b/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt index 51efbb45e..a478a8309 100644 --- a/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt +++ b/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ @file:OptIn(ExperimentalRpcApi::class) @@ -61,3 +61,15 @@ inline suspend fun fail(client: RpcClient, server: RpcServer, serviceDescriptorOf<NotAService>() serviceDescriptorOf<T>() } + +@Rpc +annotation class Grpc + +@Grpc +interface MyGrpcService + +@Grpc +class WrongGrpcTarget + +@Rpc +class WrongRpcTarget diff --git a/versions-root/kotlin-versions-lookup.csv b/versions-root/kotlin-versions-lookup.csv index cc9448d6f..4695aab06 100644 --- a/versions-root/kotlin-versions-lookup.csv +++ b/versions-root/kotlin-versions-lookup.csv @@ -1,5 +1,5 @@ Kotlin,atomicfu,serialization,detekt-gradle-plugin,gradle-kotlin-dsl,binary-compatibility-validator,kover -2.1.0,0.26.0,1.7.3,1.23.7,5.1.2,0.16.3,0.8.3 +2.1.0,0.27.0,1.8.0,1.23.7,5.1.2,0.17.0,0.9.1 2.0.21,0.26.0,1.7.3,1.23.7,5.1.2,0.16.3,0.8.3 2.0.20,0.26.0,1.7.3,1.23.7,5.1.2,0.16.3,0.8.3 2.0.10,0.26.0,1.7.1,1.23.7,5.1.2,0.16.3,0.8.3 diff --git a/versions-root/libs.versions.toml b/versions-root/libs.versions.toml index 35e36659c..904b18318 100644 --- a/versions-root/libs.versions.toml +++ b/versions-root/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # core library version -kotlinx-rpc = "0.5.0-SNAPSHOT" +kotlinx-rpc = "0.5.0" # kotlin kotlin-lang = "2.1.0" # 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.9.0" -ktor = "3.0.1" -kotlin-logging = "7.0.0" +coroutines = "1.10.1" +ktor = "3.0.3" +kotlin-logging = "7.0.3" slf4j = "2.0.16" logback = "1.3.14" gradle-plugin-publish = "1.3.0" -kotlin-wrappers = "1.0.0-pre.823" +kotlin-wrappers = "2025.1.2" junit4 = "4.13.2" -junit5 = "5.11.3" +junit5 = "5.11.4" intellij = "233.13135.128" gradle-doctor = "0.10.0" -kotlinx-browser = "0.2" -shadow-jar = "9.0.0-beta2" +kotlinx-browser = "0.3" +shadow-jar = "9.0.0-beta4" # Stub versions – relpaced based on kotlin, mostly for gradle-related (plugins) dependencies # but also for dependencies for compiler-specific modules. @@ -28,12 +28,12 @@ shadow-jar = "9.0.0-beta2" # The current version is the one that is used with the latest Kotlin. # # NOTE: When updating kotlin-versions-lookup.csv, update the latest version here for the Renovate configs -atomicfu = "0.26.0" -serialization = "1.7.3" +atomicfu = "0.27.0" +serialization = "1.8.0" detekt-gradle-plugin = "1.23.7" -gradle-kotlin-dsl = "5.1.2" -binary-compatibility-validator = "0.16.3" -kover = "0.8.3" +gradle-kotlin-dsl = "5.2.0" +binary-compatibility-validator = "0.17.0" +kover = "0.9.1" [libraries] # kotlinx.rpc – references to the included builds @@ -126,4 +126,4 @@ conventions-npm = { id = "conventions-npm", version.ref = "kotlinx-rpc" } compiler-specific-module = { id = "compiler-specific-module", version.ref = "kotlinx-rpc" } # gradle-plugin project -kotlinx-rpc = { id = "org.jetbrains.kotlinx.rpc.plugin", version.ref = "kotlinx-rpc" } +kotlinx-rpc = { id = "org.jetbrains.kotlinx.rpc.plugin" }