Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.5.1'
CONFIG_JSON_VERSION: '0.6.0'

jobs:
build:
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Example of a setup in a project's `build.gradle.kts`:
plugins {
kotlin("multiplatform") version "2.1.20"
kotlin("plugin.serialization") version "2.1.20"
id("org.jetbrains.kotlinx.rpc.plugin") version "0.5.1"
id("org.jetbrains.kotlinx.rpc.plugin") version "0.6.0"
}
```

Expand All @@ -131,15 +131,15 @@ And now you can add dependencies to your project:
```kotlin
dependencies {
// Client API
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-client:0.6.0")
// Server API
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-server:0.6.0")
// Serialization module. Also, protobuf and cbor are provided
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-serialization-json:0.6.0")

// Transport implementation for Ktor
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-client:0.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-rpc-krpc-ktor-server:0.6.0")

// Ktor API
implementation("io.ktor:ktor-client-cio-jvm:$ktor_version")
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/kotlinx-rpc/help-versions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[
{"version":"0.5.1","url":"/kotlinx-rpc/0.5.1/","isCurrent":true}
{"version":"0.6.0","url":"/kotlinx-rpc/0.6.0/","isCurrent":true}
]
5 changes: 3 additions & 2 deletions docs/pages/kotlinx-rpc/rpc.tree
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- 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.
-->

<!DOCTYPE instance-profile
Expand Down Expand Up @@ -36,10 +36,11 @@
<toc-element topic="strict-mode.topic"/>
<toc-element topic="versions.topic"/>
<toc-element toc-title="Migration guides">
<toc-element topic="0-6-0.topic"/>
<toc-element topic="0-5-0.topic"/>
<toc-element topic="0-4-0.topic"/>
<toc-element topic="0-3-0.topic"/>
<toc-element topic="0-2-4.topic"/>
<toc-element topic="0-2-1.topic"/>
</toc-element>
</instance-profile>
</instance-profile>
24 changes: 24 additions & 0 deletions docs/pages/kotlinx-rpc/topics/0-6-0.topic
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
-->

<!DOCTYPE topic
SYSTEM "https://resources.jetbrains.com/writerside/1.0/xhtml-entities.dtd">
<topic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/topic.v2.xsd"
title="Migration to 0.6.0" id="0-6-0">

<p>
Version <code>0.6.0</code> introduces non-breaking changes, that require migration.
</p>

<chapter title="Non-suspending flows" id="non-suspending-flows">
<p>
Non-suspending server flows are now supported.
That begins the deprecation cycle for the suspending server flows and stream scopes.
Please refer to the <a href="strict-mode.topic"/> section for more details on the migration
(sections <code>Non-suspending server flows</code> and <code>Stream scopes management</code>).
</p>
</chapter>
</topic>
61 changes: 5 additions & 56 deletions docs/pages/kotlinx-rpc/topics/features.topic
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- 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.
-->

<!DOCTYPE topic
Expand All @@ -25,15 +25,15 @@

@Rpc
interface MyService : RemoteService {
suspend fun sendStream(stream: Flow&lt;Int&gt;): Flow&lt;String&gt;
fun sendStream(stream: Flow&lt;Int&gt;): Flow&lt;String&gt;

suspend fun streamRequest(request: StreamRequest)
}
</code-block>

<p>
Another requirement is that server-side steaming (flows that are returned from a function),
must be the top-level type:
must be the top-level type and the function must be non-suspending:
</p>

<code-block lang="kotlin">
Expand All @@ -45,7 +45,8 @@

@Rpc
interface MyService : RemoteService {
suspend fun serverStream(): Flow&lt;String&gt; // ok
fun serverStream(): Flow&lt;String&gt; // ok
suspend fun serverStream(): Flow&lt;String&gt; // not ok
suspend fun serverStream(): StreamResult // not ok
}
</code-block>
Expand All @@ -55,57 +56,5 @@
<a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#contextual-serialization">Contextual</a>
annotation.
</note>

<p>
To use flows in your code, use the <code>streamScoped</code> function
that will provide your flows with their lifetime:
</p>

<code-block lang="kotlin">
@Rpc
interface MyService : RemoteService {
suspend fun sendFlow(flow: Flow&lt;Int&gt;)
}

val myService = rpcClient.withService&lt;MyService&gt;()

streamScoped {
val flow = flow {
repeat(10) { i -&gt;
emit(i)
}
}

myService.sendFlow(flow)
}
</code-block>
<p>
In that case all your flows, including incoming and outgoing,
will work until the <code>streamScoped</code> function completes.
After that, all streams that are still live will be closed.
</p>
<p>
You can have multiple RPC calls and flows inside the <code>streamScoped</code> function, including those from
different services.
</p>
<p>
On the server side, you can use the <code>invokeOnStreamScopeCompletion</code> handler inside your methods
to execute code after <code>streamScoped</code> on the client side has closed.
It might be useful to clean resources, for example.
</p>
<warning>
Note that this API is experimental and may be removed in future releases.
</warning>
<p>
Another way of managing streams is to do it manually.
For this, you can use the <code>StreamScope</code> constructor function together with
<code>withStreamScope</code>:
</p>
<code-block lang="kotlin">
val streamScope = StreamScope(myJob)
withStreamScope(streamScope) {
// use streams here
}
</code-block>
</chapter>
</topic>
91 changes: 90 additions & 1 deletion docs/pages/kotlinx-rpc/topics/strict-mode.topic
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
-->

<!DOCTYPE topic
SYSTEM "https://resources.jetbrains.com/writerside/1.0/xhtml-entities.dtd">
<topic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Expand Down Expand Up @@ -63,6 +67,89 @@
}
</code-block>
</li>
<li>
<b>Non-suspending server flows</b>
<p>Deprecation level: <code>WARNING</code></p>

<code-block lang="kotlin">
data class SpotifyWrapped(val extra: Data)

@Rpc
interface Service : RemoteService {
suspend fun old(): Flow&lt;SpotifyWrapped&gt; // deprecated

fun new(): Flow&lt;SpotifyWrapped&gt;
}
</code-block>
</li>
<li>
<b>Stream scopes management</b>
<p>Deprecation level: <code>WARNING</code></p>

<p>
The next stream scope management structures are deprecated due to the introduction of
non-suspending server flows:
</p>
<list>
<li><code>StreamScoped</code> class and function</li>
<li><code>streamScoped</code> function</li>
<li><code>invokeOnStreamScopeCompletion</code> function</li>
<li><code>withStreamScope</code> function</li>
</list>
<p>
Stream collection and completion is now bound to the <code>CoroutineScope</code> in which the flow was
collected (server-side flows) or produced (client-side flows).
</p>
<p>
Old code:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To have the two code blocks side-by-side, you can wrap them in a <compare> element:

<compare first-title="0.5.x" second-title="0.6.x">
      <code-block>...</code-block>
      <code-block>...</code-block>
</compare>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not look good when rendered

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it's not important to use it. I'd only recommend to change the preceding text from "Old code" and "New code" to "0.5.x" and "0.6.x"

</p>
<code-block lang="kotlin">
@Rpc
interface Service : RemoteService {
suspend fun oldClient(flow: Flow&lt;Int&gt;)
suspend fun oldServer(): Flow&lt;Int&gt;
}

suspend fun consumer(service: Service) {
streamScoped {
service.oldClient(flow { /* ... */ } )

service.oldServer().collect {
// ...
}
}
}
</code-block>
<p>
New code:
</p>
<code-block lang="kotlin">
@Rpc
interface Service : RemoteService {
suspend fun newClient(flow: Flow&lt;Int&gt;)
fun newServer(): Flow&lt;Int&gt;
}

fun consumer(service: Service, scope: CoroutineScope) {
val flow = service.new()
scope.launch {
service.newClient(flow { /* ... */ } )

flow.collect {
// ...
}
}
}
// or
suspend fun consumer(service: Service) {
service.newClient(flow { /* ... */ } )

service.new().collect {
// ...
}
}
</code-block>
</li>
</list>

<p>
Expand All @@ -81,6 +168,8 @@
nestedFlow = RpcStrictMode.WARNING
notTopLevelServerFlow = RpcStrictMode.WARNING
fields = RpcStrictMode.WARNING
suspendingServerStreaming = RpcStrictMode.WARNING
streamScopedFunctions = RpcStrictMode.WARNING
}
}
</code-block>
Expand All @@ -90,7 +179,7 @@

<warning>
Note that setting <code>RpcStrictMode.NONE</code> should not be done permanently.
All deprecated APIs will become errors in future without an option to suppress it.
All deprecated APIs will become errors in the future without an option to suppress them.
Consider your migration path in advance.
</warning>
</topic>
4 changes: 2 additions & 2 deletions docs/pages/kotlinx-rpc/v.list
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- 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.
-->

<!DOCTYPE vars SYSTEM "https://resources.jetbrains.com/writerside/1.0/vars.dtd">
Expand All @@ -14,6 +14,6 @@
<var name="host" value="https://kotlin.github.io"/>

<!-- Library versions -->
<var name="kotlinx-rpc-version" value="0.5.1"/>
<var name="kotlinx-rpc-version" value="0.6.0"/>
<var name="kotlin-version" value="2.1.20"/>
</vars>
4 changes: 2 additions & 2 deletions docs/pages/kotlinx-rpc/writerside.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- 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.
-->

<!DOCTYPE ihp SYSTEM "https://resources.jetbrains.com/writerside/1.0/ihp.dtd">
Expand All @@ -12,5 +12,5 @@
<images dir="images" web-path="images/"/>
<categories src="c.list"/>
<vars src="v.list"/>
<instance src="rpc.tree" version="0.5.1" web-path="/kotlinx-rpc/"/>
<instance src="rpc.tree" version="0.6.0" web-path="/kotlinx-rpc/"/>
</ihp>
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import kotlin.js.JsName
* Failure of one request will not cancel all streams in the others.
*/
@OptIn(InternalCoroutinesApi::class)
@Deprecated("StreamScope is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-6-0.html", level = DeprecationLevel.WARNING)
public class StreamScope internal constructor(
parentContext: CoroutineContext,
internal val role: Role,
Expand Down Expand Up @@ -170,6 +171,7 @@ public suspend fun <T> callScoped(callId: String, block: suspend CoroutineScope.
* }
* ```
*/
@Deprecated("streamScoped is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-6-0.html", level = DeprecationLevel.WARNING)
@OptIn(ExperimentalContracts::class)
public suspend fun <T> streamScoped(block: suspend CoroutineScope.() -> T): T {
contract {
Expand Down Expand Up @@ -205,6 +207,7 @@ private fun CoroutineContext.checkContextForStreamScope() {
*/
@JsName("StreamScope_fun")
@ExperimentalRpcApi
@Deprecated("StreamScoped is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-6-0.html", level = DeprecationLevel.WARNING)
public fun StreamScope(parent: CoroutineContext): StreamScope {
parent.checkContextForStreamScope()

Expand All @@ -216,6 +219,10 @@ public fun StreamScope(parent: CoroutineContext): StreamScope {
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalRpcApi
@Deprecated(
"withStreamScope is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-6-0.html",
level = DeprecationLevel.WARNING
)
public suspend fun <T> withStreamScope(scope: StreamScope, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
Expand Down Expand Up @@ -252,6 +259,10 @@ public suspend fun <T> withStreamScope(scope: StreamScope, block: suspend Corout
* ```
*/
@ExperimentalRpcApi
@Deprecated(
"invokeOnStreamScopeCompletion is deprecated, see https://kotlin.github.io/kotlinx-rpc/0-6-0.html",
level = DeprecationLevel.WARNING
)
public suspend fun invokeOnStreamScopeCompletion(throwIfNoScope: Boolean = true, block: (Throwable?) -> Unit) {
val streamScope = streamScopeOrNull() ?: noStreamScopeError()

Expand Down