diff --git a/CHANGELOG.md b/CHANGELOG.md index d71a16a..2c2b5b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Revision history for servant-event-stream +## 0.3.1.0 -- 2025-08-21 + +* Qualify `servant` imports so that `ServerSentEvents` added in `servant-0.20.3.0` does not conflict with ours. (Issue #12) + ## 0.3.0.0 -- 2024-09-05 * Breaking changes to the API. Event streams are implemented using servant's 'Stream' endpoint. You should - provide a handler that returns a stream of events that implements 'ToSourceIO' - where events have a 'ToServerEvent' instance. + provide a handler that returns a stream of events that implements + 'ToSourceIO' where events have a 'ToServerEvent' instance. Example: diff --git a/flake.lock b/flake.lock index e3ae90b..73284a8 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1733220138, - "narHash": "sha256-Yh5XZ9yVurrcYdNTSWxYgW4+EJ0pcOqgM1043z9JaRc=", + "lastModified": 1755704039, + "narHash": "sha256-gKlP0LbyJ3qX0KObfIWcp5nbuHSb5EHwIvU6UcNBg2A=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bcb68885668cccec12276bbb379f8f2557aa06ce", + "rev": "9cb344e96d5b6918e94e1bca2d9f3ea1e9615545", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-24.05", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index f3cd137..a6af320 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "servant-event-stream"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; }; outputs = diff --git a/servant-event-stream.cabal b/servant-event-stream.cabal index a653b40..7b6a88e 100644 --- a/servant-event-stream.cabal +++ b/servant-event-stream.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-event-stream -version: 0.3.0.1 +version: 0.3.1.0 stability: alpha synopsis: Servant support for Server-Sent events category: Servant, Web @@ -14,7 +14,7 @@ license: BSD3 license-file: LICENSE author: Shaun Sharples maintainer: shaun.sharples@gmail.com -copyright: (c) 2024 Shaun Sharples +copyright: (c) 2025 Shaun Sharples build-type: Simple extra-source-files: CHANGELOG.md diff --git a/src/Servant/API/EventStream.hs b/src/Servant/API/EventStream.hs index 27c09e5..f3f3c9d 100644 --- a/src/Servant/API/EventStream.hs +++ b/src/Servant/API/EventStream.hs @@ -68,9 +68,9 @@ import Data.Text (Text) import Data.Typeable (Typeable) import GHC.Generics (Generic) import Network.HTTP.Media ((//), (/:)) -import Servant -import Servant.Foreign -import Servant.Foreign.Internal (_FunctionName) +import qualified Servant as S +import qualified Servant.Foreign as S +import qualified Servant.Foreign.Internal as SFI {- | A ServerSentEvents endpoint emits an event stream using the format described at @@ -78,7 +78,7 @@ import Servant.Foreign.Internal (_FunctionName) data ServerSentEvents (a :: Type) deriving (Typeable, Generic) -instance HasLink (ServerSentEvents a) where +instance S.HasLink (ServerSentEvents a) where type MkLink (ServerSentEvents a) r = r toLink toA _ = toA @@ -100,7 +100,7 @@ data ServerEvent = ServerEvent class ToServerEvent a where toServerEvent :: a -> ServerEvent -instance (ToServerEvent a) => MimeRender EventStream a where +instance (ToServerEvent a) => S.MimeRender EventStream a where mimeRender _ = encodeServerEvent . toServerEvent {- 1. Field names must not contain LF, CR or COLON characters. @@ -124,39 +124,39 @@ encodeServerEvent e = instance ToServerEvent ServerEvent where toServerEvent = id -instance {-# OVERLAPPABLE #-} (ToServerEvent chunk, ToSourceIO chunk a) => HasServer (ServerSentEvents a) context where - type ServerT (ServerSentEvents a) m = ServerT (StreamGet ServerEventFraming EventStream a) m - route Proxy = - route - (Proxy :: Proxy (StreamGet ServerEventFraming EventStream a)) - hoistServerWithContext Proxy = - hoistServerWithContext - (Proxy :: Proxy (StreamGet ServerEventFraming EventStream a)) - -instance {-# OVERLAPPING #-} (ToServerEvent chunk, ToSourceIO chunk a, GetHeaders (Headers h a)) => HasServer (ServerSentEvents (Headers h a)) context where - type ServerT (ServerSentEvents (Headers h a)) m = ServerT (StreamGet ServerEventFraming EventStream (Headers h a)) m - route Proxy = - route - (Proxy :: Proxy (StreamGet ServerEventFraming EventStream (Headers h a))) - hoistServerWithContext Proxy = - hoistServerWithContext - (Proxy :: Proxy (StreamGet ServerEventFraming EventStream (Headers h a))) +instance {-# OVERLAPPABLE #-} (ToServerEvent chunk, S.ToSourceIO chunk a) => S.HasServer (ServerSentEvents a) context where + type ServerT (ServerSentEvents a) m = S.ServerT (S.StreamGet ServerEventFraming EventStream a) m + route S.Proxy = + S.route + (S.Proxy :: S.Proxy (S.StreamGet ServerEventFraming EventStream a)) + hoistServerWithContext S.Proxy = + S.hoistServerWithContext + (S.Proxy :: S.Proxy (S.StreamGet ServerEventFraming EventStream a)) + +instance {-# OVERLAPPING #-} (ToServerEvent chunk, S.ToSourceIO chunk a, S.GetHeaders (S.Headers h a)) => S.HasServer (ServerSentEvents (S.Headers h a)) context where + type ServerT (ServerSentEvents (S.Headers h a)) m = S.ServerT (S.StreamGet ServerEventFraming EventStream (S.Headers h a)) m + route S.Proxy = + S.route + (S.Proxy :: S.Proxy (S.StreamGet ServerEventFraming EventStream (S.Headers h a))) + hoistServerWithContext S.Proxy = + S.hoistServerWithContext + (S.Proxy :: S.Proxy (S.StreamGet ServerEventFraming EventStream (S.Headers h a))) -- | a helper instance for instance - (HasForeignType lang ftype a) => - HasForeign lang ftype (ServerSentEvents a) + (S.HasForeignType lang ftype a) => + S.HasForeign lang ftype (ServerSentEvents a) where - type Foreign ftype (ServerSentEvents a) = Req ftype + type Foreign ftype (ServerSentEvents a) = SFI.Req ftype - foreignFor lang Proxy Proxy req = + foreignFor lang S.Proxy S.Proxy req = req - & reqFuncName . _FunctionName %~ ("stream" :) - & reqMethod .~ method - & reqReturnType ?~ retType + & SFI.reqFuncName . SFI._FunctionName %~ ("stream" :) + & SFI.reqMethod .~ method + & SFI.reqReturnType ?~ retType where - retType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) - method = reflectMethod (Proxy :: Proxy 'GET) + retType = SFI.typeFor lang (S.Proxy :: S.Proxy ftype) (S.Proxy :: S.Proxy a) + method = S.reflectMethod (S.Proxy :: S.Proxy S.GET) {- | A type representation of an event stream. It's responsible for setting proper content-type and buffering headers, as well as for providing parser implementations for the streams. @@ -164,19 +164,19 @@ instance -} data EventStream -instance Accept EventStream where +instance S.Accept EventStream where contentType _ = "text" // "event-stream" /: ("charset", "utf-8") -- | Recommended headers for Server-Sent Events. -type RecommendedEventSourceHeaders (a :: Type) = Headers '[Header "X-Accel-Buffering" Text, Header "Cache-Control" Text] a +type RecommendedEventSourceHeaders (a :: Type) = S.Headers '[S.Header "X-Accel-Buffering" Text, S.Header "Cache-Control" Text] a -- | Add the recommended headers for Server-Sent Events to the response. recommendedEventSourceHeaders :: a -> RecommendedEventSourceHeaders a -recommendedEventSourceHeaders = addHeader @"X-Accel-Buffering" "no" . addHeader @"Cache-Control" "no-store" +recommendedEventSourceHeaders = S.addHeader @"X-Accel-Buffering" "no" . S.addHeader @"Cache-Control" "no-store" -- | A framing strategy for Server-Sent Events. data ServerEventFraming -- | Frames the server events by joining chunks with a newline. -instance FramingRender ServerEventFraming where +instance S.FramingRender ServerEventFraming where framingRender _ f = fmap (\x -> f x <> "\n")