From 287117f96f6217922afa55da1b162bc5f1ca66c7 Mon Sep 17 00:00:00 2001 From: pierscin <30854591+pierscin@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:57:24 +0200 Subject: [PATCH 1/4] Migrate to Scala 3.9.0 LTS Co-Authored-By: Claude Opus 5.5 --- .scala-steward.conf | 4 ++-- README.md | 2 +- build.sbt | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.scala-steward.conf b/.scala-steward.conf index 291249d..577d69d 100644 --- a/.scala-steward.conf +++ b/.scala-steward.conf @@ -4,7 +4,7 @@ updates.ignore = [ {groupId = "org.scala-lang", artifactId = "scala-compiler", version = "3."} ] updates.pin = [ - {groupId = "org.scala-lang", artifactId = "scala3-library", version = "3.3."}, - {groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = "3.3."}, + {groupId = "org.scala-lang", artifactId = "scala3-library", version = "3.9."}, + {groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = "3.9."}, {groupId = "com.typesafe.akka", version = "2.6."} ] diff --git a/README.md b/README.md index 254b664..ca95252 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Add the following dependency: "com.softwaremill.sttp.shared" %% "core" % "1.5.3" ``` -sttp-shared is available for Scala 2.12, 2.13, 3.3, Scala.JS and Scala Native. +sttp-shared is available for Scala 2.12, 2.13, 3.9 (JDK 17+), Scala.JS and Scala Native. ### Building & testing the scala-native version diff --git a/build.sbt b/build.sbt index ec390ef..6f8cfe5 100644 --- a/build.sbt +++ b/build.sbt @@ -7,7 +7,7 @@ val scala2_12 = "2.12.21" val scala2_13 = "2.13.18" val scala2 = List(scala2_12, scala2_13) val scala2alive = List(scala2_12, scala2_13) -val scala3 = List("3.3.8") +val scala3 = List("3.9.0") val akkaVersion = "2.6.20" val pekkoVersion = "1.7.0" val sttpModelVersion = "1.8.2" @@ -36,7 +36,7 @@ versionScheme := Some("semver-spec") val commonJvmSettings = Seq( scalacOptions ++= - (if (ScalaArtifacts.isScala3(scalaVersion.value)) Seq("-Yfuture-lazy-vals", "-java-output-version", "11") + (if (ScalaArtifacts.isScala3(scalaVersion.value)) Seq("-java-output-version", "17") else Seq("-release", "8")), ideSkipProject := (scalaVersion.value != scala2_13), bspEnabled := !ideSkipProject.value, @@ -118,7 +118,14 @@ lazy val core = (projectMatrix in file("core")) lazy val ws = (projectMatrix in file("ws")) .settings( name := "ws", - libraryDependencies += "com.softwaremill.sttp.model" %% "core" % sttpModelVersion + libraryDependencies += "com.softwaremill.sttp.model" %% "core" % sttpModelVersion, + scalacOptions ++= { + // TODO(scala-3.9): `Data[_]` in WebSocket.receiveDataFrame's signature should become `Data[?]`, but the + // source is shared with Scala 2.12/2.13, which don't accept `?` without -Xsource:3 + if (ScalaArtifacts.isScala3(scalaVersion.value)) + Seq("-Wconf:msg=is deprecated for wildcard arguments of types&src=.*sttp/ws/WebSocket\\.scala:s") + else Nil + } ) .jvmPlatform( scalaVersions = scala2 ++ scala3, From 3e879da52064f1ab24837690d73301fd8344f61c Mon Sep 17 00:00:00 2001 From: pierscin <30854591+pierscin@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:57:24 +0200 Subject: [PATCH 2/4] Use implicit scope instead of explicit implicit args in WebSocketStubTests Fixes Scala 3.7+ "Implicit parameters should be provided with a using clause" warnings in a way that also compiles on Scala 2.12/2.13. Co-Authored-By: Claude Opus 5.5 --- .../sttp/ws/testing/WebSocketStubTests.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ws/src/test/scala/sttp/ws/testing/WebSocketStubTests.scala b/ws/src/test/scala/sttp/ws/testing/WebSocketStubTests.scala index 72f4fb2..eddaeb7 100644 --- a/ws/src/test/scala/sttp/ws/testing/WebSocketStubTests.scala +++ b/ws/src/test/scala/sttp/ws/testing/WebSocketStubTests.scala @@ -8,14 +8,17 @@ import sttp.ws.{WebSocketClosed, WebSocketFrame} import scala.util.{Failure, Success} import sttp.monad.IdentityMonad +import sttp.shared.Identity class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { class MyException extends Exception + private implicit val identityMonad: MonadError[Identity] = IdentityMonad + "web socket stub" should "return initial Incoming frames on 'receive'" in { val frames = List("a", "b", "c").map(WebSocketFrame.text) val webSocketStub = WebSocketStub.initialReceive(frames) - val ws = webSocketStub.build(IdentityMonad) + val ws = webSocketStub.build[Identity] ws.receive() shouldBe WebSocketFrame.text("a") ws.receive() shouldBe WebSocketFrame.text("b") @@ -26,7 +29,7 @@ class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { val okFrame = WebSocketFrame.text("abc") val exception = new MyException val webSocketStub = WebSocketStub.initialReceiveWith(List(Success(okFrame), Failure(exception))) - val ws = webSocketStub.build(IdentityMonad) + val ws = webSocketStub.build[Identity] ws.receive() shouldBe WebSocketFrame.text("abc") assertThrows[MyException](ws.receive()) @@ -43,7 +46,7 @@ class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { case `expectedFrame` => List(secondFrame, thirdFrame) case _ => List.empty } - val ws = webSocketStub.build(IdentityMonad) + val ws = webSocketStub.build[Identity] ws.receive() shouldBe WebSocketFrame.text("No. 1") assertThrows[IllegalStateException](ws.receive()) // no more stubbed messages @@ -60,7 +63,7 @@ class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { val webSocketStub = WebSocketStub.noInitialReceive .thenRespondWith(_ => List(Success(ok), Failure(exception))) - val ws = webSocketStub.build(IdentityMonad) + val ws = webSocketStub.build[Identity] ws.send(WebSocketFrame.text("let's add responses")) ws.receive() shouldBe WebSocketFrame.text("ok") @@ -71,7 +74,7 @@ class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { val ok = WebSocketFrame.text("ok") val closeFrame = WebSocketFrame.Close(500, "internal error") val webSocketStub = WebSocketStub.initialReceive(List(closeFrame, ok)) - val ws = webSocketStub.build(IdentityMonad) + val ws = webSocketStub.build[Identity] ws.send(WebSocketFrame.text("let's add responses")) ws.receive() shouldBe closeFrame @@ -84,7 +87,7 @@ class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { .thenRespondS(0) { case (counter, _) => (counter + 1, List(WebSocketFrame.text(s"No. $counter"))) } - val ws = webSocketStub.build(IdentityMonad) + val ws = webSocketStub.build[Identity] ws.send(WebSocketFrame.text("a")) ws.send(WebSocketFrame.text("b")) @@ -115,7 +118,8 @@ class WebSocketStubTests extends AnyFlatSpec with Matchers with ScalaFutures { "receive" should "be lazy" in { // given - val ws = WebSocketStub.noInitialReceive.thenRespond(_ => List(WebSocketFrame.text("test"))).build(LazyMonad) + implicit val lazyMonad: MonadError[() => *] = LazyMonad + val ws = WebSocketStub.noInitialReceive.thenRespond(_ => List(WebSocketFrame.text("test"))).build[() => *] // when val doReceive = ws.receive() // should return a lazy receive From f08ecc7bcdfa1241c86dbc7210286ad5fa0b3f61 Mon Sep 17 00:00:00 2001 From: pierscin <30854591+pierscin@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:21:46 +0200 Subject: [PATCH 3/4] Fix the new Scala 3.9 wildcard warning instead of suppressing it Enable -Xsource:3 in the Scala 2 builds of ws, so the shared WebSocket source can use `?`. Co-Authored-By: Claude Opus 5.5 --- build.sbt | 8 +------- ws/src/main/scala/sttp/ws/WebSocket.scala | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 6f8cfe5..f905a14 100644 --- a/build.sbt +++ b/build.sbt @@ -119,13 +119,7 @@ lazy val ws = (projectMatrix in file("ws")) .settings( name := "ws", libraryDependencies += "com.softwaremill.sttp.model" %% "core" % sttpModelVersion, - scalacOptions ++= { - // TODO(scala-3.9): `Data[_]` in WebSocket.receiveDataFrame's signature should become `Data[?]`, but the - // source is shared with Scala 2.12/2.13, which don't accept `?` without -Xsource:3 - if (ScalaArtifacts.isScala3(scalaVersion.value)) - Seq("-Wconf:msg=is deprecated for wildcard arguments of types&src=.*sttp/ws/WebSocket\\.scala:s") - else Nil - } + scalacOptions ++= (if (ScalaArtifacts.isScala3(scalaVersion.value)) Nil else Seq("-Xsource:3")) ) .jvmPlatform( scalaVersions = scala2 ++ scala3, diff --git a/ws/src/main/scala/sttp/ws/WebSocket.scala b/ws/src/main/scala/sttp/ws/WebSocket.scala index 48c5e38..bcc6375 100644 --- a/ws/src/main/scala/sttp/ws/WebSocket.scala +++ b/ws/src/main/scala/sttp/ws/WebSocket.scala @@ -46,7 +46,7 @@ trait WebSocket[F[_]] { * @param pongOnPing * Should a [[WebSocketFrame.Pong]] be sent when a [[WebSocketFrame.Ping]] is received. */ - def receiveDataFrame(pongOnPing: Boolean = true): F[WebSocketFrame.Data[_]] = + def receiveDataFrame(pongOnPing: Boolean = true): F[WebSocketFrame.Data[?]] = receive().flatMap { case close: WebSocketFrame.Close => monad.error(WebSocketClosed(Some(close))) case d: WebSocketFrame.Data[_] => monad.unit(d) From d6c3d5dc5e7818842437d8be8f668f36a9845a1e Mon Sep 17 00:00:00 2001 From: pierscin <30854591+pierscin@users.noreply.github.com> Date: Thu, 8 Oct 2026 13:29:36 +0200 Subject: [PATCH 4/4] Remove the JDK note from the README Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca95252..3b135a3 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Add the following dependency: "com.softwaremill.sttp.shared" %% "core" % "1.5.3" ``` -sttp-shared is available for Scala 2.12, 2.13, 3.9 (JDK 17+), Scala.JS and Scala Native. +sttp-shared is available for Scala 2.12, 2.13, 3.9, Scala.JS and Scala Native. ### Building & testing the scala-native version