From 0b83b09a38035f4e2d1f74c9b4cfd8fba157a120 Mon Sep 17 00:00:00 2001
From: Alexander Sysoev
Date: Tue, 14 Jan 2025 18:45:41 +0100
Subject: [PATCH 1/3] Added deprecation notes
---
.../kotlin/kotlinx/rpc/RpcEagerField.kt | 6 ++++-
.../rpc/UninitializedRpcFieldException.kt | 6 ++++-
.../kotlinx/rpc/awaitFieldInitialization.kt | 14 +++++++++-
.../rpc/descriptor/RpcServiceDescriptor.kt | 10 ++++++-
.../kotlin/kotlinx/rpc/registerField.kt | 14 +++++++++-
.../kotlin/kotlinx/rpc/krpc/KrpcConfig.kt | 27 +++++++++++++++++--
6 files changed, 70 insertions(+), 7 deletions(-)
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/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()
}
From 52e27f0fe2fa06845291e7b996bf81729fd5d06b Mon Sep 17 00:00:00 2001
From: Alexander Sysoev
Date: Tue, 14 Jan 2025 19:43:22 +0100
Subject: [PATCH 2/3] Updated docs for the 0.5.0 release
---
.github/workflows/docs.yml | 2 +-
README.md | 37 ++-
docs/pages/kotlinx-rpc/.idea/kotlinx-rpc.iml | 8 +
docs/pages/kotlinx-rpc/.idea/modules.xml | 8 +
docs/pages/kotlinx-rpc/help-versions.json | 2 +-
docs/pages/kotlinx-rpc/rpc.tree | 4 +
docs/pages/kotlinx-rpc/topics/0-5-0.topic | 166 ++++++++++++
.../topics/annotation-type-safety.topic | 72 ++++++
.../kotlinx-rpc/topics/configuration.topic | 4 +
docs/pages/kotlinx-rpc/topics/features.topic | 237 ++++++------------
.../topics/service-descriptors.topic | 21 ++
docs/pages/kotlinx-rpc/topics/services.topic | 4 +-
.../kotlinx-rpc/topics/strict-mode.topic | 96 +++++++
docs/pages/kotlinx-rpc/v.list | 2 +-
docs/pages/kotlinx-rpc/writerside.cfg | 2 +-
15 files changed, 493 insertions(+), 172 deletions(-)
create mode 100644 docs/pages/kotlinx-rpc/.idea/kotlinx-rpc.iml
create mode 100644 docs/pages/kotlinx-rpc/.idea/modules.xml
create mode 100644 docs/pages/kotlinx-rpc/topics/0-5-0.topic
create mode 100644 docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic
create mode 100644 docs/pages/kotlinx-rpc/topics/service-descriptors.topic
create mode 100644 docs/pages/kotlinx-rpc/topics/strict-mode.topic
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 9cc8b7380..8a3e63011 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -20,7 +20,7 @@ env:
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/README.md b/README.md
index e046720bb..ad7a96be6 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")
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..0d5f9bc64 100644
--- a/docs/pages/kotlinx-rpc/rpc.tree
+++ b/docs/pages/kotlinx-rpc/rpc.tree
@@ -14,8 +14,10 @@
+
+
@@ -24,8 +26,10 @@
+
+
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..2fafc6d2a
--- /dev/null
+++ b/docs/pages/kotlinx-rpc/topics/0-5-0.topic
@@ -0,0 +1,166 @@
+
+
+
+
+
+ Version 0.5.0
introduces breaking changes.
+
+
+
+
+ This release brings annotation type-safety.
+ This means that some green code technically can become red:
+
+
+ @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
+
+
+ Thar should not be a problem, as this code was wrong in both releases anyway and would fail in runtime in 0.4.0
.
+
+
+
+
+
+ This release introduces Strict Mode.
+ Some service declarations are now deprecated, currently with a warning.
+ In the following releases it will change to an error.
+
+
+ Deprecated service APIs:
+
+
+ StateFlow
and SharedFlow
usage
+ Fields in services
+ Nested Flows
+ Not top-level server side flows
+
+
+ More info on the Strict Mode page.
+
+
+
+
+ The following APIs were renamed to satisfy Kotlin style guides and bring even more consistency to the library:
+
+
+ Old |
+ New |
+
+
+ kotlinx.rpc.RPCClient |
+ kotlinx.rpc.RpcClient |
+
+
+ kotlinx.rpc.RPCServer |
+ kotlinx.rpc.RpcServer |
+
+
+ kotlinx.rpc.UninitializedRPCFieldException |
+ kotlinx.rpc.UninitializedRpcFieldException |
+
+
+ kotlinx.rpc.RPCEagerField |
+ kotlinx.rpc.RpcEagerField |
+
+
+ kotlinx.rpc.RPCEagerField |
+ kotlinx.rpc.RpcEagerField |
+
+
+ kotlinx.rpc.internal.utils.InternalRPCApi |
+ kotlinx.rpc.internal.utils.InternalRpcApi |
+
+
+ kotlinx.rpc.internal.utils.ExperimentalRPCApi |
+ kotlinx.rpc.internal.utils.ExperimentalRpcApi |
+
+
+ kotlinx.rpc.krpc.RPCConfig |
+ kotlinx.rpc.krpc.KrpcConfig |
+
+
+ kotlinx.rpc.krpc.RPCConfigBuilder |
+ kotlinx.rpc.krpc.KrpcConfigBuilder |
+
+
+ kotlinx.rpc.krpc.RPCTransport |
+ kotlinx.rpc.krpc.KrpcTransport |
+
+
+ kotlinx.rpc.krpc.RPCTransportMessage |
+ kotlinx.rpc.krpc.KrpcTransportMessage |
+
+
+ kotlinx.rpc.krpc.client.KRPCClient |
+ kotlinx.rpc.krpc.client.KrpcClient |
+
+
+ kotlinx.rpc.krpc.server.KRPCServer |
+ kotlinx.rpc.krpc.server.KrpcServer |
+
+
+ kotlinx.rpc.krpc.ktor.client.installRPC |
+ kotlinx.rpc.krpc.ktor.client.installKrpc |
+
+
+ kotlinx.rpc.krpc.ktor.client.RPC |
+ kotlinx.rpc.krpc.ktor.client.Krpc |
+
+
+ kotlinx.rpc.krpc.ktor.client.KtorRPCClient |
+ kotlinx.rpc.krpc.ktor.client.KtorRpcClient |
+
+
+ kotlinx.rpc.krpc.ktor.server.RPC |
+ kotlinx.rpc.krpc.ktor.server.Krpc |
+
+
+ kotlinx.rpc.krpc.ktor.server.RPCRoute |
+ kotlinx.rpc.krpc.ktor.server.KrpcRoute |
+
+
+ kotlinx.rpc.krpc.serialization.RPCSerialFormat |
+ kotlinx.rpc.krpc.serialization.KrpcSerialFormat |
+
+
+ kotlinx.rpc.krpc.serialization.RPCSerialFormatBuilder |
+ kotlinx.rpc.krpc.serialization.KrpcSerialFormatBuilder |
+
+
+ kotlinx.rpc.krpc.serialization.RPCSerialFormatConfiguration |
+ kotlinx.rpc.krpc.serialization.KrpcSerialFormatConfiguration |
+
+
+ kotlinx.rpc.krpc.serialization.cbor.RPCCborSerialFormat |
+ kotlinx.rpc.krpc.serialization.cbor.KrpcCborSerialFormat |
+
+
+ kotlinx.rpc.krpc.serialization.json.RPCJsonSerialFormat |
+ kotlinx.rpc.krpc.serialization.json.KrpcJsonSerialFormat |
+
+
+ kotlinx.rpc.krpc.serialization.protobuf.RPCProtobufSerialFormat |
+ kotlinx.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..b5797f225
--- /dev/null
+++ b/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Library introduces a concept of annotation type-safety. Consider the following example:
+
+
+ @Rpc
+ interface MyService : RemoteService
+
+ class MyServiceImpl : MyService
+
+ fun <T : RemoteService> withService() {}
+
+
+ Compiler can not guarantee, that the passed type is the one for which the code generation was run:
+
+
+ withService<MyService>() // ok
+ withService<MyServiceImpl>() // compile time ok, runtime throws
+
+
+ Our compiler plugin, however, can make this code behave as expected.
+
+
+ @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) {}
+
+ // ok, T is resolved to MyService,
+ // but 'body' parameter returns MyServiceImpl
+ registerService<MyService> { ctx -> MyServiceImpl(ctx) }
+
+ // error, T is resolved to MyServiceImpl
+ registerService { ctx -> MyServiceImpl(ctx) }
+
+
+
+
+ The feature is highly experimental.
+ The concept will stay, however due to the complexity of the implementation,
+ our compiler plugin may behave unexpectedly when performing type-safety checks.
+ Please, report any encountered bugs.
+
+ To ensure critical bugs don't paralyze your app, there is a kill-switch present for the feature:
+
+ // 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..42439045f 100644
--- a/docs/pages/kotlinx-rpc/topics/configuration.topic
+++ b/docs/pages/kotlinx-rpc/topics/configuration.topic
@@ -53,6 +53,10 @@
sharedFlowParameters
DSL
+
+ These parameters are deprecated since 0.5.0
, 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..d960423a8 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.
+ This is only applied to Flow
type, StateFlow
and SharedFlow
+ are not supported and there are no plans to do so.
+
- 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 limitation 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
+ 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 - you need to use special 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 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.
+
+
+ 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/service-descriptors.topic b/docs/pages/kotlinx-rpc/topics/service-descriptors.topic
new file mode 100644
index 000000000..47d2a6f0a
--- /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 to access entities generated by the compiler plugin.
+ API can be accessed using the following code:
+
+
+ serviceDescriptorOf<MyService>()
+
+
+ The list of available entities can be found in 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..7db71a950
--- /dev/null
+++ b/docs/pages/kotlinx-rpc/topics/strict-mode.topic
@@ -0,0 +1,96 @@
+
+
+
+
+
+ Starting with version 0.5.0
library undergoes major change in supported 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 @@
-
+
From 12ff49457bb9d59848bfad515dbf8bb73c9bd422 Mon Sep 17 00:00:00 2001
From: Alexander Sysoev
Date: Fri, 17 Jan 2025 16:16:58 +0100
Subject: [PATCH 3/3] Review comments
---
docs/pages/kotlinx-rpc/topics/0-5-0.topic | 13 +++++-----
.../topics/annotation-type-safety.topic | 25 ++++++++++---------
.../kotlinx-rpc/topics/configuration.topic | 3 ++-
docs/pages/kotlinx-rpc/topics/features.topic | 20 +++++++--------
.../topics/service-descriptors.topic | 6 ++---
.../kotlinx-rpc/topics/strict-mode.topic | 10 ++++----
6 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/docs/pages/kotlinx-rpc/topics/0-5-0.topic b/docs/pages/kotlinx-rpc/topics/0-5-0.topic
index 2fafc6d2a..0e480eb37 100644
--- a/docs/pages/kotlinx-rpc/topics/0-5-0.topic
+++ b/docs/pages/kotlinx-rpc/topics/0-5-0.topic
@@ -11,8 +11,8 @@
- This release brings annotation type-safety.
- This means that some green code technically can become red:
+ This release introduces annotation type-safety.
+ As a result, some code that previously compiled successfully may now produce errors:
@Rpc
@@ -28,15 +28,16 @@
withService<MyServiceImpl>() // ok in 0.4.0, error in 0.5.0
- Thar should not be a problem, as this code was wrong in both releases anyway and would fail in runtime in 0.4.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, currently with a warning.
- In the following releases it will change to an error.
+ Some service declarations are now deprecated with a warning.
+ In upcoming releases, these warnings will be replaced with errors.
Deprecated service APIs:
@@ -48,7 +49,7 @@
Not top-level server side flows
- More info on the Strict Mode page.
+ For more information, see the Strict Mode documentation.
diff --git a/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic b/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic
index b5797f225..135146a7b 100644
--- a/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic
+++ b/docs/pages/kotlinx-rpc/topics/annotation-type-safety.topic
@@ -6,7 +6,7 @@
title="Annotation type-safety" id="annotation-type-safety">
- Library introduces a concept of annotation type-safety. Consider the following example:
+ The library introduces a concept of annotation type-safety. Consider the following example:
@Rpc
@@ -17,14 +17,15 @@
fun <T : RemoteService> withService() {}
- Compiler can not guarantee, that the passed type is the one for which the code generation was run:
+ 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
- Our compiler plugin, however, can make this code behave as expected.
+ The compiler plugin enforces annotation type-safety by requiring type parameters to have specific annotations,
+ like @Rpc
.
@Rpc
@@ -41,27 +42,27 @@
- Annotation type safety only ensures that the resolved type parameters are annotated with the required annotation.
+ 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) {}
- // ok, T is resolved to MyService,
- // but 'body' parameter returns MyServiceImpl
+ // T is resolved to MyService,
+ // but 'body' returns MyServiceImpl
registerService<MyService> { ctx -> MyServiceImpl(ctx) }
- // error, T is resolved to MyServiceImpl
+ // Error: T is resolved to MyServiceImpl
registerService { ctx -> MyServiceImpl(ctx) }
- The feature is highly experimental.
- The concept will stay, however due to the complexity of the implementation,
- our compiler plugin may behave unexpectedly when performing type-safety checks.
- Please, report any encountered bugs.
+ This feature is highly experimental and may lead to unexpected behaviour. If you encounter any issues,
+ please report them.
- To ensure critical bugs don't paralyze your app, there is a kill-switch present for the feature:
+ To prevent critical bugs from affecting your app, you can disable this feature using the following
+ configuration:
// build.gradle.kts
rpc {
diff --git a/docs/pages/kotlinx-rpc/topics/configuration.topic b/docs/pages/kotlinx-rpc/topics/configuration.topic
index 42439045f..0b382bd18 100644
--- a/docs/pages/kotlinx-rpc/topics/configuration.topic
+++ b/docs/pages/kotlinx-rpc/topics/configuration.topic
@@ -54,7 +54,8 @@
- These parameters are deprecated since 0.5.0
, see the migration guide.
+ These parameters are deprecated since 0.5.0
. For more information,
+ see the migration guide.
diff --git a/docs/pages/kotlinx-rpc/topics/features.topic b/docs/pages/kotlinx-rpc/topics/features.topic
index d960423a8..54b5830ac 100644
--- a/docs/pages/kotlinx-rpc/topics/features.topic
+++ b/docs/pages/kotlinx-rpc/topics/features.topic
@@ -12,8 +12,8 @@
You can send and receive Kotlin Flows in RPC
methods.
- This is only applied to Flow
type, StateFlow
and SharedFlow
- are not supported and there are no plans to do so.
+ However, this only applies to the Flow
type. StateFlow
and SharedFlow
+ are not supported, and there are no plans to add support for them.
@@ -32,7 +32,7 @@
- Another limitation is that server-side steaming (flows that are returned from a function),
+ Another requirement is that server-side steaming (flows that are returned from a function),
must be the top-level type:
@@ -51,13 +51,13 @@
- Note that flows that are declared in classes (like in StreamResult
) require
+ Note that flows that are declared in classes (like in StreamResult
) require a
Contextual
annotation.
- To use flows in your code - you need to use special streamScoped
function
+ To use flows in your code, use the streamScoped
function
that will provide your flows with their lifetime:
@@ -85,17 +85,17 @@
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
+ You can have multiple RPC calls and flows inside the streamScoped
function, including those from
different services.
- On server side, you can use invokeOnStreamScopeCompletion
handler inside your methods
- to execute code after streamScoped
on client side has closed.
+ 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
diff --git a/docs/pages/kotlinx-rpc/topics/service-descriptors.topic b/docs/pages/kotlinx-rpc/topics/service-descriptors.topic
index 47d2a6f0a..421c1b9c8 100644
--- a/docs/pages/kotlinx-rpc/topics/service-descriptors.topic
+++ b/docs/pages/kotlinx-rpc/topics/service-descriptors.topic
@@ -9,13 +9,13 @@
This API is experimental and may be changed at any time.
- Service Descriptors allow to access entities generated by the compiler plugin.
- API can be accessed using the following code:
+ Service Descriptors allow you to access entities generated by the compiler plugin.
+ You can access them by using the following code:
serviceDescriptorOf<MyService>()
- The list of available entities can be found in code
+ For the list of available entities, refer to the source code.
diff --git a/docs/pages/kotlinx-rpc/topics/strict-mode.topic b/docs/pages/kotlinx-rpc/topics/strict-mode.topic
index 7db71a950..18e8c46a1 100644
--- a/docs/pages/kotlinx-rpc/topics/strict-mode.topic
+++ b/docs/pages/kotlinx-rpc/topics/strict-mode.topic
@@ -6,12 +6,12 @@
title="Strict mode" id="strict-mode">
- Starting with version 0.5.0
library undergoes major change in supported service APIs.
+ 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
+ StateFlow
and SharedFlow
Deprecation level: WARNING
@Rpc
@@ -66,7 +66,7 @@
- Deprecation levels are controlled by the Gradle `rpc` extension:
+ Deprecation levels are controlled by the Gradle rpc
extension:
// build.gradle.kts
@@ -88,9 +88,9 @@
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.
-
+