on 09de487 help sign in
servant-reflex.cabal+4−2
since splitsnapshots
unifiedsplit
@@ -27,6 +27,7 @@ library
27 hs-source-dirs: src
27 hs-source-dirs: src
28 build-depends:
28 build-depends:
29 base >= 4.8 && < 4.10,
29 base >= 4.8 && < 4.10,
30+ aeson >= 0.11 && < 1.3,
30 bytestring >= 0.10 && < 0.11,
31 bytestring >= 0.10 && < 0.11,
31 case-insensitive >= 1.2.0.4 && < 1.3,
32 case-insensitive >= 1.2.0.4 && < 1.3,
32 containers >= 0.5.6 && < 0.6,
33 containers >= 0.5.6 && < 0.6,
@@ -41,8 +42,9 @@ library
41 reflex >= 0.5 && < 0.6,
42 reflex >= 0.5 && < 0.6,
42 reflex-dom-core == 0.4 && < 0.5,
43 reflex-dom-core == 0.4 && < 0.5,
43 safe >= 0.3.9 && < 0.4,
44 safe >= 0.3.9 && < 0.4,
44− servant >= 0.8 && < 0.12,
45+ servant >= 0.13 && < 0.14,
46+ servant-checked-exceptions >= 1.1.0.0 && < 1.2,
45− servant-auth >= 0.2.1 && < 0.3,
47+ servant-auth >= 0.2.1 && < 0.4,
46 string-conversions >= 0.4 && < 0.5,
48 string-conversions >= 0.4 && < 0.5,
47 text >= 1.2 && < 1.3,
49 text >= 1.2 && < 1.3,
48 transformers >= 0.4 && < 0.6
50 transformers >= 0.4 && < 0.6
src/Servant/Common/Req.hs+65−19
@@ -8,6 +8,8 @@
8{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TupleSections #-}
9{-# LANGUAGE TupleSections #-}
10
10
11+{-# LANGUAGE MultiParamTypeClasses #-}
12+{-# LANGUAGE FlexibleInstances #-}
11module Servant.Common.Req where
13module Servant.Common.Req where
12
14
13-------------------------------------------------------------------------------
15-------------------------------------------------------------------------------
@@ -15,8 +17,11 @@ import Control.Applicative (liftA2, liftA3)
15import Control.Concurrent
17import Control.Concurrent
16import Control.Monad (join)
18import Control.Monad (join)
17import Control.Monad.IO.Class (MonadIO, liftIO)
19import Control.Monad.IO.Class (MonadIO, liftIO)
20+import Data.Aeson
18import Data.Bifunctor (first)
21import Data.Bifunctor (first)
19−import qualified Data.ByteString.Builder as Builder
22+import qualified Data.ByteString.Builder as BB
23+import qualified Data.ByteString as BS
24+import Data.ByteString.Lazy (ByteString)
20import qualified Data.ByteString.Lazy.Char8 as BL
25import qualified Data.ByteString.Lazy.Char8 as BL
21import qualified Data.Map as Map
26import qualified Data.Map as Map
22import Data.Maybe (catMaybes, fromMaybe)
27import Data.Maybe (catMaybes, fromMaybe)
@@ -25,17 +30,24 @@ import Data.Monoid ((<>))
25import Data.Proxy (Proxy(..))
30import Data.Proxy (Proxy(..))
26import Data.Text (Text)
31import Data.Text (Text)
27import qualified Data.Text as T
32import qualified Data.Text as T
28−import qualified Data.Text.Encoding as TE
33+import qualified Data.Text.Lazy as TL
34+import qualified Data.Text.Encoding as T
29import Data.Traversable (forM)
35import Data.Traversable (forM)
30import Language.Javascript.JSaddle.Monad (JSM, MonadJSM, liftJSM)
36import Language.Javascript.JSaddle.Monad (JSM, MonadJSM, liftJSM)
37+import qualified Language.Javascript.JSaddle as JS
38+import qualified Data.JSString.Text as JS (lazyTextFromJSString)
31import qualified Network.URI as N
39import qualified Network.URI as N
32import Reflex.Dom.Core hiding (tag)
40import Reflex.Dom.Core hiding (tag)
33import Servant.Common.BaseUrl (BaseUrl, showBaseUrl,
41import Servant.Common.BaseUrl (BaseUrl, showBaseUrl,
34 SupportsServantReflex)
42 SupportsServantReflex)
35−import Servant.API.ContentTypes (MimeUnrender(..), NoContent(..))
43+import Servant.API.ContentTypes ( NoContent(..), Accept, JSON
44+ , OctetStream, PlainText
45+ , FormUrlEncoded)
36import Web.HttpApiData (ToHttpApiData(..))
46import Web.HttpApiData (ToHttpApiData(..))
47+import Web.FormUrlEncoded (FromForm, urlDecodeAsForm)
37-------------------------------------------------------------------------------
48-------------------------------------------------------------------------------
38import Servant.API.BasicAuth
49import Servant.API.BasicAuth
50+-------------------------------------------------------------------------------
39
51
40
52
41------------------------------------------------------------------------------
53------------------------------------------------------------------------------
@@ -150,7 +162,7 @@ prependToPathParts p req =
150 req { reqPathParts = p : reqPathParts req }
162 req { reqPathParts = p : reqPathParts req }
151
163
152addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t -> Req t
164addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t -> Req t
153−addHeader name val req = req { headers = (name, (fmap . fmap) (TE.decodeUtf8 . toHeader) val) : headers req }
165+addHeader name val req = req { headers = (name, (fmap . fmap) (T.decodeUtf8 . toHeader) val) : headers req }
154
166
155
167
156reqToReflexRequest
168reqToReflexRequest
@@ -245,8 +257,8 @@ reqToReflexRequest reqMeth reqHost req =
245 mkAuth _ (Left e) = Left e
257 mkAuth _ (Left e) = Left e
246 mkAuth Nothing r = r
258 mkAuth Nothing r = r
247 mkAuth (Just (BasicAuthData u p)) (Right config) = Right $ config
259 mkAuth (Just (BasicAuthData u p)) (Right config) = Right $ config
248− { _xhrRequestConfig_user = Just $ TE.decodeUtf8 u
260+ { _xhrRequestConfig_user = Just $ T.decodeUtf8 u
249− , _xhrRequestConfig_password = Just $ TE.decodeUtf8 p}
261+ , _xhrRequestConfig_password = Just $ T.decodeUtf8 p}
250
262
251 addAuth :: Dynamic t (Either Text (XhrRequestConfig x))
263 addAuth :: Dynamic t (Either Text (XhrRequestConfig x))
252 -> Dynamic t (Either Text (XhrRequestConfig x))
264 -> Dynamic t (Either Text (XhrRequestConfig x))
@@ -326,10 +338,9 @@ performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs
326 return ()
338 return ()
327
339
328
340
329−
330type XhrPayload = T.Text
341type XhrPayload = T.Text
331bytesToPayload :: BL.ByteString -> XhrPayload
342bytesToPayload :: BL.ByteString -> XhrPayload
332−bytesToPayload = TE.decodeUtf8 . BL.toStrict
343+bytesToPayload = T.decodeUtf8 . BL.toStrict
333
344
334
345
335performRequestsCT
346performRequestsCT
@@ -344,11 +355,10 @@ performRequestsCT
344 -> m (Event t (f (ReqResult tag a)))
355 -> m (Event t (f (ReqResult tag a)))
345performRequestsCT ct reqMeth reqs reqHost opts trigger = do
356performRequestsCT ct reqMeth reqs reqHost opts trigger = do
346 resps <- performRequests reqMeth reqs reqHost opts trigger
357 resps <- performRequests reqMeth reqs reqHost opts trigger
347− let decodeResp x = first T.pack .
358+ let decodeResp x = first T.pack
348− mimeUnrender ct .
359+ . mimeUnrender ct
349− BL.fromStrict .
360+ . JS.textToJSString
350− TE.encodeUtf8 =<< note "No body text"
351− (_xhrResponse_responseText x)
361+ =<< note "No body text" (_xhrResponse_responseText x)
352 return $ fmap
362 return $ fmap
353 (\(t,rs) -> ffor rs $ \r -> case r of
363 (\(t,rs) -> ffor rs $ \r -> case r of
354 Left e -> RequestFailure t e
364 Left e -> RequestFailure t e
@@ -379,7 +389,7 @@ evalResponse
379 :: (XhrResponse -> Either Text a)
389 :: (XhrResponse -> Either Text a)
380 -> (tag, XhrResponse)
390 -> (tag, XhrResponse)
381 -> ReqResult tag a
391 -> ReqResult tag a
382−evalResponse decode (tag, xhr) =
392+evalResponse decodeRes (tag, xhr) =
383 let okStatus = _xhrResponse_status xhr < 400
393 let okStatus = _xhrResponse_status xhr < 400
384 errMsg = fromMaybe
394 errMsg = fromMaybe
385 ("Empty response with error code " <>
395 ("Empty response with error code " <>
@@ -389,20 +399,56 @@ evalResponse decode (tag, xhr) =
389 then either
399 then either
390 (\e -> ResponseFailure tag e xhr)
400 (\e -> ResponseFailure tag e xhr)
391 (\v -> ResponseSuccess tag v xhr)
401 (\v -> ResponseSuccess tag v xhr)
392− (decode xhr)
402+ (decodeRes xhr)
393 else ResponseFailure tag errMsg xhr
403 else ResponseFailure tag errMsg xhr
394 in respPayld
404 in respPayld
395
405
396
406
407+-- | Similar to 'Servant.API.ContentType.MimeUnrender' but with the differnce that
408+-- the second argument expects a JSString instead of ByteString.
409+-- This can lead to better performance ghcjs.
410+class Accept contentType => MimeUnrender contentType a where
411+ mimeUnrender :: Proxy contentType -> JS.JSString -> Either String a
412+
413+instance FromJSON a => MimeUnrender JSON a where
414+ mimeUnrender _ = note "Failed to decode response" . jsonDecode
415+
416+-- | @urlDecodeAsForm@
417+-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
418+-- holds if every element of x is non-null (i.e., not @("", "")@)
419+instance FromForm a => MimeUnrender FormUrlEncoded a where
420+ mimeUnrender _ = first T.unpack . urlDecodeAsForm . BB.toLazyByteString . T.encodeUtf8Builder . JS.textFromJSString
421+
422+-- | @left show . TextL.decodeUtf8'@
423+instance MimeUnrender PlainText TL.Text where
424+ mimeUnrender _ = pure . JS.lazyTextFromJSString
425+
426+-- | @left show . TextS.decodeUtf8' . toStrict@
427+instance MimeUnrender PlainText T.Text where
428+ mimeUnrender _ = pure . JS.textFromJSString
429+
430+-- | @Right . BC.unpack@
431+instance MimeUnrender PlainText String where
432+ mimeUnrender _ = Right . T.unpack . JS.textFromJSString
433+
434+-- | @Right . id@
435+instance MimeUnrender OctetStream ByteString where
436+ mimeUnrender _ = pure . BB.toLazyByteString . T.encodeUtf8Builder . JS.textFromJSString
437+
438+-- | @Right . toStrict@
439+instance MimeUnrender OctetStream BS.ByteString where
440+ mimeUnrender _ = pure . T.encodeUtf8 . JS.textFromJSString
441+
397note :: e -> Maybe a -> Either e a
442note :: e -> Maybe a -> Either e a
398−note e = maybe (Left e) Right
443+note e Nothing = Left e
444+note _ (Just a) = Right a
399
445
400fmapL :: (e -> e') -> Either e a -> Either e' a
446fmapL :: (e -> e') -> Either e a -> Either e' a
401fmapL _ (Right a) = Right a
447fmapL _ (Right a) = Right a
402fmapL f (Left e) = Left (f e)
448fmapL f (Left e) = Left (f e)
403
449
404−builderToText :: Builder.Builder -> T.Text
450+builderToText :: BB.Builder -> T.Text
405−builderToText = TE.decodeUtf8 . BL.toStrict . Builder.toLazyByteString
451+builderToText = T.decodeUtf8 . BL.toStrict . BB.toLazyByteString
406
452
407escape :: T.Text -> T.Text
453escape :: T.Text -> T.Text
408−escape = T.pack . N.escapeURIString (not . N.isReserved) . T.unpack . TE.decodeUtf8 . BL.toStrict . Builder.toLazyByteString . toEncodedUrlPiece
454+escape = T.pack . N.escapeURIString (not . N.isReserved) . T.unpack . T.decodeUtf8 . BL.toStrict . BB.toLazyByteString . toEncodedUrlPiece
src/Servant/Reflex.hs+137−2
@@ -51,7 +51,7 @@ import Servant.API ((:<|>)(..),(:>), BasicAuth,
51 BasicAuthData, BuildHeadersTo(..),
51 BasicAuthData, BuildHeadersTo(..),
52 Capture, contentType, Header,
52 Capture, contentType, Header,
53 Headers(..), HttpVersion, IsSecure,
53 Headers(..), HttpVersion, IsSecure,
54− MimeRender(..), MimeUnrender,
54+ MimeRender(..),
55 NoContent, QueryFlag, QueryParam,
55 NoContent, QueryFlag, QueryParam,
56 QueryParams, Raw, ReflectMethod(..),
56 QueryParams, Raw, ReflectMethod(..),
57 RemoteHost, ReqBody,
57 RemoteHost, ReqBody,
@@ -68,7 +68,7 @@ import Reflex.Dom.Core (Dynamic, Event, Reflex,
68import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
68import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
69 showBaseUrl,
69 showBaseUrl,
70 SupportsServantReflex)
70 SupportsServantReflex)
71−import Servant.Common.Req (ClientOptions(..),
71+import Servant.Common.Req (ClientOptions(..), MimeUnrender(..),
72 defaultClientOptions,
72 defaultClientOptions,
73 Req, ReqResult(..), QParam(..),
73 Req, ReqResult(..), QParam(..),
74 QueryPart(..), addHeader, authData,
74 QueryPart(..), addHeader, authData,
@@ -84,6 +84,8 @@ import Servant.Common.Req (ClientOptions(..),
84 response,
84 response,
85 reqTag,
85 reqTag,
86 qParams, withCredentials)
86 qParams, withCredentials)
87+import Servant.Checked.Exceptions.Internal.Envelope (Envelope)
88+import Servant.Checked.Exceptions.Internal.Servant.API (NoThrow, Throws, Throwing, ThrowingNonterminal)
87
89
88
90
89-- * Accessing APIs as a Client
91-- * Accessing APIs as a Client
@@ -571,3 +573,136 @@ type family HasCookieAuth xs :: Constraint where
571
573
572class CookieAuthNotEnabled
574class CookieAuthNotEnabled
573
575
576+{- servant-checked-exceptions
577+~~~~~~~~~~~~~~~~~~~~~
578+-}
579+
580+-- | Change a 'Throws' into 'Throwing'.
581+instance (HasClient t m (Throwing '[e] :> api) tag) => HasClient t m (Throws e :> api) tag where
582+ type Client t m (Throws e :> api) tag = Client t m (Throwing '[e] :> api) tag
583+
584+ clientWithRoute
585+ :: Proxy (Throws e :> api)
586+ -> Proxy m
587+ -> Proxy tag
588+ -> Req t
589+ -> Dynamic t BaseUrl
590+ -> ClientOptions
591+ -> Client t m (Throwing '[e] :> api) tag
592+ clientWithRoute _ = clientWithRoute (Proxy :: Proxy (Throwing '[e] :> api))
593+
594+-- | When @'Throwing' es@ comes before a 'Verb', change it into the same 'Verb'
595+-- but returning an @'Envelope' es@.
596+instance (HasClient t m (Verb method status ctypes (Envelope es a)) tag) =>
597+ HasClient t m (Throwing es :> Verb method status ctypes a) tag where
598+
599+ type Client t m (Throwing es :> Verb method status ctypes a) tag =
600+ Client t m (Verb method status ctypes (Envelope es a)) tag
601+
602+ clientWithRoute
603+ :: Proxy (Throwing es :> Verb method status ctypes a)
604+ -> Proxy m
605+ -> Proxy tag
606+ -> Req t
607+ -> Dynamic t BaseUrl
608+ -> ClientOptions
609+ -> Client t m (Verb method status ctypes (Envelope es a)) tag
610+ clientWithRoute Proxy =
611+ clientWithRoute (Proxy :: Proxy (Verb method status ctypes (Envelope es a)))
612+
613+-- | When 'NoThrow' comes before a 'Verb', change it into the same 'Verb'
614+-- but returning an @'Envelope' \'[]@.
615+instance (HasClient t m (Verb method status ctypes (Envelope '[] a)) tag) =>
616+ HasClient t m (NoThrow :> Verb method status ctypes a) tag where
617+
618+ type Client t m (NoThrow :> Verb method status ctypes a) tag =
619+ Client t m (Verb method status ctypes (Envelope '[] a)) tag
620+
621+ clientWithRoute
622+ :: Proxy (NoThrow :> Verb method status ctypes a)
623+ -> Proxy m
624+ -> Proxy tag
625+ -> Req t
626+ -> Dynamic t BaseUrl
627+ -> ClientOptions
628+ -> Client t m (Verb method status ctypes (Envelope '[] a)) tag
629+ clientWithRoute Proxy =
630+ clientWithRoute (Proxy :: Proxy (Verb method status ctypes (Envelope '[] a)))
631+
632+-- | When @'Throwing' es@ comes before ':<|>', push @'Throwing' es@ into each
633+-- branch of the API.
634+instance HasClient t m ((Throwing es :> api1) :<|> (Throwing es :> api2)) tag =>
635+ HasClient t m (Throwing es :> (api1 :<|> api2)) tag where
636+
637+ type Client t m (Throwing es :> (api1 :<|> api2)) tag =
638+ Client t m ((Throwing es :> api1) :<|> (Throwing es :> api2)) tag
639+
640+ clientWithRoute
641+ :: Proxy (Throwing es :> (api1 :<|> api2))
642+ -> Proxy m
643+ -> Proxy tag
644+ -> Req t
645+ -> Dynamic t BaseUrl
646+ -> ClientOptions
647+ -> Client t m ((Throwing es :> api1) :<|> (Throwing es :> api2)) tag
648+ clientWithRoute _ =
649+ clientWithRoute (Proxy :: Proxy ((Throwing es :> api1) :<|> (Throwing es :> api2)))
650+
651+-- | When 'NoThrow' comes before ':<|>', push 'NoThrow' into each branch of the
652+-- API.
653+instance HasClient t m ((NoThrow :> api1) :<|> (NoThrow :> api2)) tag =>
654+ HasClient t m (NoThrow :> (api1 :<|> api2)) tag where
655+
656+ type Client t m (NoThrow :> (api1 :<|> api2)) tag =
657+ Client t m ((NoThrow :> api1) :<|> (NoThrow :> api2)) tag
658+
659+ clientWithRoute
660+ :: Proxy (NoThrow :> (api1 :<|> api2))
661+ -> Proxy m
662+ -> Proxy tag
663+ -> Req t
664+ -> Dynamic t BaseUrl
665+ -> ClientOptions
666+ -> Client t m ((NoThrow :> api1) :<|> (NoThrow :> api2)) tag
667+ clientWithRoute _ =
668+ clientWithRoute (Proxy :: Proxy ((NoThrow :> api1) :<|> (NoThrow :> api2)))
669+
670+-- | When a @'Throws' e@ comes immediately after a @'Throwing' es@, 'Snoc' the
671+-- @e@ onto the @es@. Otherwise, if @'Throws' e@ comes before any other
672+-- combinator, push it down so it is closer to the 'Verb'.
673+instance HasClient t m (ThrowingNonterminal (Throwing es :> api :> apis)) tag =>
674+ HasClient t m (Throwing es :> api :> apis) tag where
675+
676+ type Client t m (Throwing es :> api :> apis) tag =
677+ Client t m (ThrowingNonterminal (Throwing es :> api :> apis)) tag
678+
679+ clientWithRoute
680+ :: Proxy (Throwing es :> api :> apis)
681+ -> Proxy m
682+ -> Proxy tag
683+ -> Req t
684+ -> Dynamic t BaseUrl
685+ -> ClientOptions
686+ -> Client t m (ThrowingNonterminal (Throwing es :> api :> apis)) tag
687+ clientWithRoute _ =
688+ clientWithRoute (Proxy :: Proxy (ThrowingNonterminal (Throwing es :> api :> apis)))
689+
690+-- | When 'NoThrow' comes before any other combinator, push it down so it is
691+-- closer to the 'Verb'.
692+instance HasClient t m (api :> NoThrow :> apis) tag =>
693+ HasClient t m (NoThrow :> api :> apis) tag where
694+
695+ type Client t m (NoThrow :> api :> apis) tag =
696+ Client t m (api :> NoThrow :> apis) tag
697+
698+ clientWithRoute
699+ :: Proxy (NoThrow :> api :> apis)
700+ -> Proxy m
701+ -> Proxy tag
702+ -> Req t
703+ -> Dynamic t BaseUrl
704+ -> ClientOptions
705+ -> Client t m (api :> NoThrow :> apis) tag
706+ clientWithRoute _ =
707+ clientWithRoute (Proxy :: Proxy (api :> NoThrow :> apis))
708+
src/Servant/Reflex/Multi.hs+144−2
@@ -49,7 +49,7 @@ import Servant.API ((:<|>) (..), (:>), BasicAuth,
49 BasicAuthData, BuildHeadersTo (..),
49 BasicAuthData, BuildHeadersTo (..),
50 Capture, Header, Headers (..),
50 Capture, Header, Headers (..),
51 HttpVersion, IsSecure, MimeRender (..),
51 HttpVersion, IsSecure, MimeRender (..),
52− MimeUnrender, NoContent, QueryFlag,
52+ NoContent, QueryFlag,
53 QueryParam, QueryParams, Raw,
53 QueryParam, QueryParams, Raw,
54 ReflectMethod (..), RemoteHost,
54 ReflectMethod (..), RemoteHost,
55 ReqBody, ToHttpApiData (..), Vault,
55 ReqBody, ToHttpApiData (..), Vault,
@@ -62,7 +62,7 @@ import Reflex.Dom.Core (Dynamic, Event, Reflex,
62------------------------------------------------------------------------------
62------------------------------------------------------------------------------
63import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
63import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
64 SupportsServantReflex)
64 SupportsServantReflex)
65−import Servant.Common.Req (ClientOptions,
65+import Servant.Common.Req (ClientOptions, MimeUnrender,
66 QParam (..), QueryPart (..), Req,
66 QParam (..), QueryPart (..), Req,
67 ReqResult (..), addHeader, authData,
67 ReqResult (..), addHeader, authData,
68 defReq,
68 defReq,
@@ -74,6 +74,8 @@ import Servant.Common.Req (ClientOptions,
74 qParams, reqBody, reqFailure,
74 qParams, reqBody, reqFailure,
75 reqMethod, reqSuccess, reqSuccess',
75 reqMethod, reqSuccess, reqSuccess',
76 respHeaders, response, withCredentials)
76 respHeaders, response, withCredentials)
77+import Servant.Checked.Exceptions.Internal.Envelope (Envelope)
78+import Servant.Checked.Exceptions.Internal.Servant.API (NoThrow, Throws, Throwing, ThrowingNonterminal)
77import Servant.Reflex (BuildHeaderKeysTo (..), toHeaders)
79import Servant.Reflex (BuildHeaderKeysTo (..), toHeaders)
78
80
79
81
@@ -386,3 +388,143 @@ instance (HasClientMulti t m api f tag, Reflex t, Applicative f)
386 where
388 where
387 req' a r = r { authData = Just (constDyn a) }
389 req' a r = r { authData = Just (constDyn a) }
388 reqs' = liftA2 req' <$> authdatas <*> reqs
390 reqs' = liftA2 req' <$> authdatas <*> reqs
391+
392+{- servant-checked-exceptions
393+~~~~~~~~~~~~~~~~~~~~~
394+-}
395+
396+-- | Change a 'Throws' into 'Throwing'.
397+instance (HasClientMulti t m (Throwing '[e] :> api) f tag) => HasClientMulti t m (Throws e :> api) f tag where
398+ type ClientMulti t m (Throws e :> api) f tag = ClientMulti t m (Throwing '[e] :> api) f tag
399+
400+ clientWithRouteMulti
401+ :: Proxy (Throws e :> api)
402+ -> Proxy m
403+ -> Proxy f
404+ -> Proxy tag
405+ -> Dynamic t (f (Req t))
406+ -> Dynamic t BaseUrl
407+ -> ClientOptions
408+ -> ClientMulti t m (Throwing '[e] :> api) f tag
409+ clientWithRouteMulti _ = clientWithRouteMulti (Proxy :: Proxy (Throwing '[e] :> api))
410+
411+-- | When @'Throwing' es@ comes before a 'Verb', change it into the same 'Verb'
412+-- but returning an @'Envelope' es@.
413+instance (HasClientMulti t m (Verb method status ctypes (Envelope es a)) f tag) =>
414+ HasClientMulti t m (Throwing es :> Verb method status ctypes a) f tag where
415+
416+ type ClientMulti t m (Throwing es :> Verb method status ctypes a) f tag =
417+ ClientMulti t m (Verb method status ctypes (Envelope es a)) f tag
418+
419+ clientWithRouteMulti
420+ :: Proxy (Throwing es :> Verb method status ctypes a)
421+ -> Proxy m
422+ -> Proxy f
423+ -> Proxy tag
424+ -> Dynamic t (f (Req t))
425+ -> Dynamic t BaseUrl
426+ -> ClientOptions
427+ -> ClientMulti t m (Verb method status ctypes (Envelope es a)) f tag
428+ clientWithRouteMulti Proxy =
429+ clientWithRouteMulti (Proxy :: Proxy (Verb method status ctypes (Envelope es a)))
430+
431+-- | When 'NoThrow' comes before a 'Verb', change it into the same 'Verb'
432+-- but returning an @'Envelope' \'[]@.
433+instance (HasClientMulti t m (Verb method status ctypes (Envelope '[] a)) f tag) =>
434+ HasClientMulti t m (NoThrow :> Verb method status ctypes a) f tag where
435+
436+ type ClientMulti t m (NoThrow :> Verb method status ctypes a) f tag =
437+ ClientMulti t m (Verb method status ctypes (Envelope '[] a)) f tag
438+
439+ clientWithRouteMulti
440+ :: Proxy (NoThrow :> Verb method status ctypes a)
441+ -> Proxy m
442+ -> Proxy f
443+ -> Proxy tag
444+ -> Dynamic t (f (Req t))
445+ -> Dynamic t BaseUrl
446+ -> ClientOptions
447+ -> ClientMulti t m (Verb method status ctypes (Envelope '[] a)) f tag
448+ clientWithRouteMulti Proxy =
449+ clientWithRouteMulti (Proxy :: Proxy (Verb method status ctypes (Envelope '[] a)))
450+
451+-- | When @'Throwing' es@ comes before ':<|>', push @'Throwing' es@ into each
452+-- branch of the API.
453+instance HasClientMulti t m ((Throwing es :> api1) :<|> (Throwing es :> api2)) f tag =>
454+ HasClientMulti t m (Throwing es :> (api1 :<|> api2)) f tag where
455+
456+ type ClientMulti t m (Throwing es :> (api1 :<|> api2)) f tag =
457+ ClientMulti t m ((Throwing es :> api1) :<|> (Throwing es :> api2)) f tag
458+
459+ clientWithRouteMulti
460+ :: Proxy (Throwing es :> (api1 :<|> api2))
461+ -> Proxy m
462+ -> Proxy f
463+ -> Proxy tag
464+ -> Dynamic t (f (Req t))
465+ -> Dynamic t BaseUrl
466+ -> ClientOptions
467+ -> ClientMulti t m ((Throwing es :> api1) :<|> (Throwing es :> api2)) f tag
468+ clientWithRouteMulti _ =
469+ clientWithRouteMulti (Proxy :: Proxy ((Throwing es :> api1) :<|> (Throwing es :> api2)))
470+
471+-- | When 'NoThrow' comes before ':<|>', push 'NoThrow' into each branch of the
472+-- API.
473+instance HasClientMulti t m ((NoThrow :> api1) :<|> (NoThrow :> api2)) f tag =>
474+ HasClientMulti t m (NoThrow :> (api1 :<|> api2)) f tag where
475+
476+ type ClientMulti t m (NoThrow :> (api1 :<|> api2)) f tag =
477+ ClientMulti t m ((NoThrow :> api1) :<|> (NoThrow :> api2)) f tag
478+
479+ clientWithRouteMulti
480+ :: Proxy (NoThrow :> (api1 :<|> api2))
481+ -> Proxy m
482+ -> Proxy f
483+ -> Proxy tag
484+ -> Dynamic t (f (Req t))
485+ -> Dynamic t BaseUrl
486+ -> ClientOptions
487+ -> ClientMulti t m ((NoThrow :> api1) :<|> (NoThrow :> api2)) f tag
488+ clientWithRouteMulti _ =
489+ clientWithRouteMulti (Proxy :: Proxy ((NoThrow :> api1) :<|> (NoThrow :> api2)))
490+
491+-- | When a @'Throws' e@ comes immediately after a @'Throwing' es@, 'Snoc' the
492+-- @e@ onto the @es@. Otherwise, if @'Throws' e@ comes before any other
493+-- combinator, push it down so it is closer to the 'Verb'.
494+instance HasClientMulti t m (ThrowingNonterminal (Throwing es :> api :> apis)) f tag =>
495+ HasClientMulti t m (Throwing es :> api :> apis) f tag where
496+
497+ type ClientMulti t m (Throwing es :> api :> apis) f tag =
498+ ClientMulti t m (ThrowingNonterminal (Throwing es :> api :> apis)) f tag
499+
500+ clientWithRouteMulti
501+ :: Proxy (Throwing es :> api :> apis)
502+ -> Proxy m
503+ -> Proxy f
504+ -> Proxy tag
505+ -> Dynamic t (f (Req t))
506+ -> Dynamic t BaseUrl
507+ -> ClientOptions
508+ -> ClientMulti t m (ThrowingNonterminal (Throwing es :> api :> apis)) f tag
509+ clientWithRouteMulti _ =
510+ clientWithRouteMulti (Proxy :: Proxy (ThrowingNonterminal (Throwing es :> api :> apis)))
511+
512+-- | When 'NoThrow' comes before any other combinator, push it down so it is
513+-- closer to the 'Verb'.
514+instance HasClientMulti t m (api :> NoThrow :> apis) f tag =>
515+ HasClientMulti t m (NoThrow :> api :> apis) f tag where
516+
517+ type ClientMulti t m (NoThrow :> api :> apis) f tag =
518+ ClientMulti t m (api :> NoThrow :> apis) f tag
519+
520+ clientWithRouteMulti
521+ :: Proxy (NoThrow :> api :> apis)
522+ -> Proxy m
523+ -> Proxy f
524+ -> Proxy tag
525+ -> Dynamic t (f (Req t))
526+ -> Dynamic t BaseUrl
527+ -> ClientOptions
528+ -> ClientMulti t m (api :> NoThrow :> apis) f tag
529+ clientWithRouteMulti _ =
530+ clientWithRouteMulti (Proxy :: Proxy (api :> NoThrow :> apis))