diff --git a/Package.resolved b/Package.resolved index 1174fda..e789b8a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "448fed2bf5c2c951c858ea31de7c40837395a88d421195f724413e544a8725bb", "pins" : [ { "identity" : "async-http-client", @@ -15,7 +16,7 @@ "location" : "https://github.com/akbashev/cluster-event-sourcing.git", "state" : { "branch" : "main", - "revision" : "6ef0b386944a2c5748ec18592065a077c72c0691" + "revision" : "a52ba54e1a6591a17156806da3087851ad60ecb2" } }, { @@ -23,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/combine-schedulers", "state" : { - "revision" : "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", - "version" : "1.0.0" + "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13", + "version" : "1.0.2" } }, { @@ -32,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/hummingbird-project/hummingbird.git", "state" : { - "revision" : "aed1e369c331f2c20780317e657d139e20522ada", - "version" : "2.0.0-beta.5" + "revision" : "6c568da113a7abe712e4a00883a85ff7745d6929", + "version" : "2.0.0" } }, { @@ -50,8 +51,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/postgres-nio.git", "state" : { - "revision" : "69ccfdf4c80144d845e3b439961b7ec6cd7ae33f", - "version" : "1.20.2" + "revision" : "cd5318a01a1efcb1e0b3c82a0ce5c9fefaf1cb2d", + "version" : "1.22.1" } }, { @@ -68,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { - "revision" : "46989693916f56d1186bd59ac15124caef896560", - "version" : "1.3.1" + "revision" : "41982a3656a71c768319979febd796c6fd111d5c", + "version" : "1.5.0" } }, { @@ -104,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "8d712376c99fc0267aa0e41fea732babe365270a", - "version" : "1.3.3" + "revision" : "642e6aab8e03e5f992d9c83e38c5be98cfad5078", + "version" : "1.5.5" } }, { @@ -113,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-clocks", "state" : { - "revision" : "d1fd837326aa719bee979bdde1f53cd5797443eb", - "version" : "1.0.0" + "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53", + "version" : "1.0.5" } }, { @@ -140,8 +141,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-composable-architecture.git", "state" : { - "revision" : "433a23118f739078644ebeb4009e23d307af694a", - "version" : "1.10.4" + "revision" : "8013f1a72af8ccb2b1735d7aed831a8dc07c6fd0", + "version" : "1.15.0" } }, { @@ -167,8 +168,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-custom-dump", "state" : { - "revision" : "f01efb26f3a192a0e88dcdb7c3c391ec2fc25d9c", - "version" : "1.3.0" + "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", + "version" : "1.3.3" } }, { @@ -176,8 +177,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "350e1e119babe8525f9bd155b76640a5de270184", - "version" : "1.3.0" + "revision" : "fd1fb25b68fdb9756cd61d23dbd9e2614b340085", + "version" : "1.4.0" } }, { @@ -186,7 +187,7 @@ "location" : "https://github.com/akbashev/swift-distributed-actors.git", "state" : { "branch" : "plugin_lifecycle_hook", - "revision" : "747ad8d91bd73ff4f478c9eb7cd668254c8804a5" + "revision" : "97994cf5be8861a057179cfab5e161ab117253b0" } }, { @@ -212,8 +213,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-identified-collections", "state" : { - "revision" : "d1e45f3e1eee2c9193f5369fa9d70a6ddad635e8", - "version" : "1.0.0" + "revision" : "2f5ab6e091dd032b63dacbda052405756010dc3b", + "version" : "1.1.0" } }, { @@ -234,6 +235,15 @@ "version" : "2.4.1" } }, + { + "identity" : "swift-navigation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-navigation", + "state" : { + "revision" : "d1bdbd8a5d1d1dfd2e4bb1f5e2f6facb631404d4", + "version" : "2.2.1" + } + }, { "identity" : "swift-nio", "kind" : "remoteSourceControl", @@ -293,8 +303,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-openapi-generator.git", "state" : { - "revision" : "7992d77065f2787e7651cf6d9be9b99ad38f5166", - "version" : "1.2.1" + "revision" : "d5f6a6abf18549c8bae6526bf2c9d5773269c570", + "version" : "1.3.1" } }, { @@ -302,8 +312,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swift-server/swift-openapi-hummingbird.git", "state" : { - "revision" : "9686d8af504d81110a80f2db1ffa161819cb456b", - "version" : "2.0.0-beta.1" + "revision" : "14dd2e2e54eb3720888b77a46cc9603fa72f7136", + "version" : "2.0.0" } }, { @@ -311,8 +321,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-openapi-runtime.git", "state" : { - "revision" : "9a8291fa2f90cc7296f2393a99bb4824ee34f869", - "version" : "1.4.0" + "revision" : "26e8ae3515d1ff3607e924ac96fc0094775f55e8", + "version" : "1.5.0" } }, { @@ -320,8 +330,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-openapi-urlsession.git", "state" : { - "revision" : "6efbfda5276bbbc8b4fec5d744f0ecd8c784eb47", - "version" : "1.0.1" + "revision" : "9bf4c712ad7989d6a91dbe68748b8829a50837e4", + "version" : "1.0.2" + } + }, + { + "identity" : "swift-perception", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-perception", + "state" : { + "revision" : "bc67aa8e461351c97282c2419153757a446ae1c9", + "version" : "1.3.5" } }, { @@ -356,26 +375,26 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swift-server/swift-service-lifecycle.git", "state" : { - "revision" : "d7fe0e731499a8dcce53bf4cbbc812c8e565d3a7", - "version" : "2.4.1" + "revision" : "24c800fb494fbee6e42bc156dc94232dc08971af", + "version" : "2.6.1" } }, { - "identity" : "swift-system", + "identity" : "swift-syntax", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-system.git", + "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "025bcb1165deab2e20d4eaba79967ce73013f496", - "version" : "1.2.1" + "revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82", + "version" : "510.0.3" } }, { - "identity" : "swiftui-navigation", + "identity" : "swift-system", "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swiftui-navigation", + "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "2ec6c3a15293efff6083966b38439a4004f25565", - "version" : "1.3.0" + "revision" : "025bcb1165deab2e20d4eaba79967ce73013f496", + "version" : "1.2.1" } }, { @@ -383,8 +402,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2", - "version" : "1.1.2" + "revision" : "bc2a151366f2cd0e347274544933bc2acb00c9fe", + "version" : "1.4.0" } }, { @@ -397,5 +416,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index e4f9cb1..dee35e3 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,8 +6,8 @@ import PackageDescription var package = Package( name: "swift-chat", platforms: [ - .macOS("14.0"), - .iOS("17.0"), + .macOS(.v14), + .iOS(.v17), ], products: [ .library(name: "App", targets: ["App"]) @@ -16,23 +16,24 @@ var package = Package( // Apple .package(url: "https://github.com/akbashev/swift-distributed-actors.git", branch: "plugin_lifecycle_hook"), .package(url: "https://github.com/akbashev/cluster-event-sourcing.git", branch: "main"), - .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), - .package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.2.1"), - .package(url: "https://github.com/apple/swift-openapi-runtime.git", from: "1.0.0"), - .package(url: "https://github.com/apple/swift-openapi-urlsession.git", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"), + .package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.3.1"), + .package(url: "https://github.com/apple/swift-openapi-runtime.git", from: "1.5.0"), + .package(url: "https://github.com/apple/swift-openapi-urlsession.git", from: "1.0.2"), .package(url: "https://github.com/apple/swift-async-algorithms.git", from: "1.0.0"), // Hummingbird - .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0-beta.2"), - .package(url: "https://github.com/swift-server/swift-openapi-hummingbird.git", from: "2.0.0-beta.1"), + .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), + .package(url: "https://github.com/swift-server/swift-openapi-hummingbird.git", from: "2.0.0"), // Vapor - .package(url: "https://github.com/vapor/postgres-nio.git", from: "1.18.0"), + .package(url: "https://github.com/vapor/postgres-nio.git", from: "1.22.1"), // Pointfree.co - .package(url: "https://github.com/pointfreeco/swift-composable-architecture.git", from: "1.10.0"), - .package(url: "https://github.com/pointfreeco/swift-dependencies.git", from: "1.3.0"), - ] + .package(url: "https://github.com/pointfreeco/swift-composable-architecture.git", from: "1.15.0"), + .package(url: "https://github.com/pointfreeco/swift-dependencies.git", from: "1.4.0"), + ], + swiftLanguageModes: [.v6] ) -var targets: [PackageDescription.Target] = [ +package.targets = [ .target( name: "API", dependencies: [ @@ -96,11 +97,3 @@ var targets: [PackageDescription.Target] = [ ] ), ] - -for target in targets { - var settings = target.swiftSettings ?? [] - settings.append(.enableExperimentalFeature("StrictConcurrency")) - target.swiftSettings = settings -} - -package.targets = targets diff --git a/Sources/App/Clients/ChatClient.swift b/Sources/App/Clients/ChatClient.swift index 80f482b..7915e73 100644 --- a/Sources/App/Clients/ChatClient.swift +++ b/Sources/App/Clients/ChatClient.swift @@ -95,7 +95,7 @@ public actor ChatClient { private var heartbeatTask: Task? private func heartbeat() { self.heartbeatTask = Task { - for await message in heartbeatSequence { + for await _ in heartbeatSequence { for (info, connection) in self.connections { connection.yield( Message( diff --git a/Sources/App/Features/Entrance/Entrance.swift b/Sources/App/Features/Entrance/Entrance.swift index 331daff..b20e6e0 100644 --- a/Sources/App/Features/Entrance/Entrance.swift +++ b/Sources/App/Features/Entrance/Entrance.swift @@ -88,6 +88,7 @@ public struct Entrance: Reducer { state.room = .init(user: state.user!, room: response) return .none case .createUser(let userName): + let client = self.client return .run { send in await send( .didCreateUser( @@ -103,6 +104,7 @@ public struct Entrance: Reducer { state.sheet = .createRoom return .none case .createRoom(let name, let description): + let client = self.client return .run { send in await send( .didCreateRoom( @@ -123,6 +125,7 @@ public struct Entrance: Reducer { } case .searchRoom(let query): state.isLoading = true + let client = self.client return .run { send in await send( .didSearchRoom( @@ -178,8 +181,9 @@ public struct Entrance: Reducer { else { return .none } + let chatClient = self.chatClient return .run { send in - await self.chatClient.disconnect( + await chatClient.disconnect( user: user, from: room ) diff --git a/Sources/Backend/Room.swift b/Sources/Backend/Room.swift index 82c0611..c990dcf 100644 --- a/Sources/Backend/Room.swift +++ b/Sources/Backend/Room.swift @@ -88,14 +88,14 @@ public distributed actor Room: EventSourced, VirtualActor { } private func notifyEveryoneAbout(_ envelope: MessageEnvelope) async { - let onlineUsers = self.state.onlineUsers - await withTaskGroup(of: Void.self) { group in - for other in onlineUsers { + let room = self + await withTaskGroup(of: Void.self) { [state] group in + for other in state.onlineUsers { group.addTask { [weak other] in // TODO: should we handle errors here? try? await other?.receive( envelopes: [envelope], - from: self + from: room ) } await group.waitForAll() diff --git a/Sources/EventSource/MemoryEventStore.swift b/Sources/EventSource/MemoryEventStore.swift index 9b6e124..4090c23 100644 --- a/Sources/EventSource/MemoryEventStore.swift +++ b/Sources/EventSource/MemoryEventStore.swift @@ -2,7 +2,7 @@ import Foundation import DistributedCluster import EventSourcing -public class MemoryEventStore: EventStore { +public actor MemoryEventStore: EventStore { private var dict: [PersistenceID: [Data]] = [:] private let encoder: JSONEncoder = JSONEncoder() diff --git a/Sources/Server/Node.swift b/Sources/Server/Node.swift index f931a40..588ac05 100644 --- a/Sources/Server/Node.swift +++ b/Sources/Server/Node.swift @@ -25,14 +25,6 @@ struct Node: AsyncParsableCommand { case room } - static let plugins: [any Plugin] = [ - ClusterSingletonPlugin(), - ClusterJournalPlugin { _ in - MemoryEventStore() - }, - ClusterVirtualActorsPlugin() - ] - @Argument var cluster: Cluster @Option var host: String = Node.defaultHost @Option var port: Int = Node.defaultPort @@ -101,7 +93,14 @@ extension Node { extension ClusterSystemSettings { mutating func installPlugins() { - for plugin in Node.plugins { self += plugin } + let plugins: [any Plugin] = [ + ClusterSingletonPlugin(), + ClusterJournalPlugin { _ in + MemoryEventStore() + }, + ClusterVirtualActorsPlugin() + ] + for plugin in plugins { self += plugin } } } diff --git a/Sources/VirtualActor/VirtualActorFactory.swift b/Sources/VirtualActor/VirtualActorFactory.swift index 00e454f..a291c39 100644 --- a/Sources/VirtualActor/VirtualActorFactory.swift +++ b/Sources/VirtualActor/VirtualActorFactory.swift @@ -58,7 +58,7 @@ distributed actor VirtualActorFactory: LifecycleWatch, ClusterSingleton { distributed func close( with id: ClusterSystem.ActorID ) async { - await withTaskGroup(of: Void.self) { group in + await withTaskGroup(of: Void.self) { [virtualNodes] group in for virtualNode in virtualNodes { group.addTask { try? await virtualNode.close(with: id)