@@ -1,46 +1,65 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFunctor #-}
2+{-# LANGUAGE DeriveFunctor #-}
3+{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE GADTs #-}
8+{-# LANGUAGE LambdaCase #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE NamedFieldPuns #-}
5−{-# LANGUAGE OverloadedStrings #-}
11+{-# LANGUAGE OverloadedStrings #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE RankNTypes #-}
12+{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TupleSections #-}
14+{-# LANGUAGE TupleSections #-}
15+{-# OPTIONS_GHC -fno-warn-orphans #-}
11module Servant.Common.Req where
17module Servant.Common.Req where
13-------------------------------------------------------------------------------
19-------------------------------------------------------------------------------
14−import Control.Applicative (liftA2, liftA3)
20+import Control.Applicative (liftA2, liftA3)
15−import Control.Arrow ((&&&))
16import Control.Concurrent
21import Control.Concurrent
17−import Control.Monad (join)
22+import Control.Monad (join)
18−import Control.Monad.IO.Class (MonadIO, liftIO)
23+import Control.Monad.IO.Class (MonadIO, liftIO)
19−import Data.Bifunctor (first)
25+import Data.Bifunctor (first)
20−import qualified Data.ByteString.Builder as Builder
26+import qualified Data.ByteString as BS
27+import qualified Data.ByteString.Builder as BB
28+import Data.ByteString.Lazy (ByteString)
21−import qualified Data.ByteString.Lazy.Char8 as BL
29+import qualified Data.ByteString.Lazy.Char8 as BL
22−import Data.ByteString (ByteString)
23−import qualified Data.Map as Map
24−import Data.Maybe (catMaybes, fromMaybe)
25import Data.Functor.Compose
30import Data.Functor.Compose
31+import qualified Data.JSString.Text as JS (lazyTextFromJSString)
32+import qualified Data.Map as Map
33+import Data.Maybe (catMaybes, fromMaybe)
26−import Data.Monoid ((<>))
34+import Data.Monoid ((<>))
27−import Data.Proxy (Proxy(..))
35+import Data.Proxy (Proxy (..))
28−import Data.Text (Text)
36+import Data.Text (Text)
29−import qualified Data.Text as T
37+import qualified Data.Text as T
30−import qualified Data.Text.Encoding as TE
38+import qualified Data.Text.Encoding as T
39+import qualified Data.Text.Lazy as TL
31−import Data.Traversable (forM)
40+import Data.Traversable (forM)
41+import qualified Language.Javascript.JSaddle as JS
32import Language.Javascript.JSaddle.Monad (JSM, MonadJSM, liftJSM)
42import Language.Javascript.JSaddle.Monad (JSM, MonadJSM, liftJSM)
33−import qualified Network.URI as N
43+import qualified Network.URI as N
34−import Reflex.Dom.Core hiding (tag)
44+import Reflex.Dom.Core hiding (tag)
35−import Servant.Common.BaseUrl (BaseUrl, showBaseUrl,
45+import Servant.API.ContentTypes (Accept, FormUrlEncoded,
36− SupportsServantReflex)
46+ JSON, NoContent (..),
37−import Servant.API.ContentTypes (MimeUnrender(..), NoContent(..))
47+ OctetStream, PlainText)
48+import Servant.Common.BaseUrl (BaseUrl,
49+ SupportsServantReflex,
51+import Web.FormUrlEncoded (FromForm, urlDecodeAsForm)
38−import Web.HttpApiData (ToHttpApiData(..))
52+import Web.HttpApiData (ToHttpApiData (..))
39-------------------------------------------------------------------------------
53-------------------------------------------------------------------------------
40import Servant.API.BasicAuth
54import Servant.API.BasicAuth
55+-------------------------------------------------------------------------------
58+-- jsonDecode :: JS.JSString -> Maybe a
59+-- jsonDecode = error "jsonDecode is only available in GHCJS"
43−------------------------------------------------------------------------------
62+-----------------------------------------------------------------------------
44-- | The result of a request event
63-- | The result of a request event
45data ReqResult tag a
64data ReqResult tag a
46 = ResponseSuccess tag a XhrResponse
65 = ResponseSuccess tag a XhrResponse
@@ -93,13 +112,6 @@ reqFailure (ResponseFailure _ s _) = Just s
93reqFailure (RequestFailure _ s) = Just s
112reqFailure (RequestFailure _ s) = Just s
94reqFailure _ = Nothing
113reqFailure _ = Nothing
96−------------------------------------------------------------------------------
97−-- | Simple filter/accessor like 'reqFailure', but keeping the request tag
98−reqFailure' :: ReqResult tag a -> Maybe (tag,Text)
99−reqFailure' (ResponseFailure tag s _) = Just (tag,s)
100−reqFailure' (RequestFailure tag s) = Just (tag,s)
101−reqFailure' _ = Nothing
104------------------------------------------------------------------------------
116------------------------------------------------------------------------------
105-- | Simple filter/accessor for the raw XHR response
117-- | Simple filter/accessor for the raw XHR response
@@ -141,34 +153,35 @@ data QueryPart t = QueryPartParam (Dynamic t (Either Text (Maybe Text)))
141-------------------------------------------------------------------------------
153-------------------------------------------------------------------------------
142-- The data structure used to build up request information while traversing
154-- The data structure used to build up request information while traversing
143-- the shape of a servant API
155-- the shape of a servant API
144−data Req t = Req
156+data Req t = forall o. (IsXhrPayload o, Show o) => Req
145 { reqMethod :: Text
157 { reqMethod :: Text
146 , reqPathParts :: [Dynamic t (Either Text Text)]
158 , reqPathParts :: [Dynamic t (Either Text Text)]
147 , qParams :: [(Text, QueryPart t)]
159 , qParams :: [(Text, QueryPart t)]
148− , reqBody :: Maybe (Dynamic t (Either Text (BL.ByteString, Text)))
160+ , reqBody :: Maybe (Dynamic t (Either Text (o, Text)))
149 , headers :: [(Text, Dynamic t (Either Text Text))]
161 , headers :: [(Text, Dynamic t (Either Text Text))]
150 , respHeaders :: XhrResponseHeaders
162 , respHeaders :: XhrResponseHeaders
151 , authData :: Maybe (Dynamic t (Maybe BasicAuthData))
163 , authData :: Maybe (Dynamic t (Maybe BasicAuthData))
166+data SomeXhrRequest = forall o. (IsXhrPayload o, Show o) => SomeXhrRequest (XhrRequest (Maybe o))
154defReq :: Req t
168defReq :: Req t
155−defReq = Req "GET" [] [] Nothing [] def Nothing
169+defReq = Req "GET" [] [] (Nothing :: Maybe (Dynamic t (Either Text ((), Text)))) [] def Nothing
157prependToPathParts :: Dynamic t (Either Text Text) -> Req t -> Req t
171prependToPathParts :: Dynamic t (Either Text Text) -> Req t -> Req t
158prependToPathParts p req =
172prependToPathParts p req =
159 req { reqPathParts = p : reqPathParts req }
173 req { reqPathParts = p : reqPathParts req }
161addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t -> Req t
175addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t -> Req t
162−addHeader name val req = req { headers = (name, (fmap . fmap) (TE.decodeUtf8 . toHeader) val) : headers req }
176+addHeader name val req = req { headers = (name, (fmap . fmap) (T.decodeUtf8 . toHeader) val) : headers req }
165reqToReflexRequest
178reqToReflexRequest
166− :: forall t. Reflex t
179+ :: forall t. Reflex t
168− -> Dynamic t BaseUrl
181+ -> Dynamic t BaseUrl
169− -> Req t
182+ -> Req t
170− -> Dynamic t (Either Text (XhrRequest XhrPayload))
183+ -> Dynamic t (Either Text SomeXhrRequest)
171−reqToReflexRequest reqMeth reqHost req =
184+reqToReflexRequest reqMeth reqHost req@(Req _ _ _ (reqBody :: Maybe (Dynamic t (Either Text (o, Text)))) _ _ _) =
172 let t :: Dynamic t [Either Text Text]
185 let t :: Dynamic t [Either Text Text]
173 t = sequence $ reverse $ reqPathParts req
186 t = sequence $ reverse $ reqPathParts req
@@ -220,33 +233,33 @@ reqToReflexRequest reqMeth reqHost req =
220 fmap (fmap (\rightVal -> (headerName, rightVal))) dynam
233 fmap (fmap (\rightVal -> (headerName, rightVal))) dynam
222 mkConfigBody :: Either Text [(Text,Text)]
235 mkConfigBody :: Either Text [(Text,Text)]
223− -> (Either Text (BL.ByteString, Text))
236+ -> (Either Text (o, Text))
224− -> Either Text (XhrRequestConfig XhrPayload)
237+ -> Either Text (XhrRequestConfig (Maybe o))
225 mkConfigBody ehs rb = case (ehs, rb) of
238 mkConfigBody ehs rb = case (ehs, rb) of
226 (_, Left e) -> Left e
239 (_, Left e) -> Left e
227 (Left e, _) -> Left e
240 (Left e, _) -> Left e
228 (Right hs, Right (bBytes, bCT)) ->
241 (Right hs, Right (bBytes, bCT)) ->
229 Right $ XhrRequestConfig
242 Right $ XhrRequestConfig
230− { _xhrRequestConfig_sendData = bytesToPayload bBytes
243+ { _xhrRequestConfig_sendData = Just bBytes
231 , _xhrRequestConfig_headers =
244 , _xhrRequestConfig_headers =
232 Map.insert "Content-Type" bCT (Map.fromList hs)
245 Map.insert "Content-Type" bCT (Map.fromList hs)
233 , _xhrRequestConfig_user = Nothing
246 , _xhrRequestConfig_user = Nothing
234 , _xhrRequestConfig_password = Nothing
247 , _xhrRequestConfig_password = Nothing
235− , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
248+ , _xhrRequestConfig_responseType = Nothing
236 , _xhrRequestConfig_withCredentials = False
249 , _xhrRequestConfig_withCredentials = False
237 , _xhrRequestConfig_responseHeaders = def
250 , _xhrRequestConfig_responseHeaders = def
240− xhrOpts :: Dynamic t (Either Text (XhrRequestConfig XhrPayload))
253+ xhrOpts :: Dynamic t (Either Text (XhrRequestConfig (Maybe o)))
241− xhrOpts = case reqBody req of
254+ xhrOpts = case reqBody of
242 Nothing -> ffor xhrHeaders $ \case
255 Nothing -> ffor xhrHeaders $ \case
243 Left e -> Left e
256 Left e -> Left e
244 Right hs -> Right $ def { _xhrRequestConfig_headers = Map.fromList hs
257 Right hs -> Right $ def { _xhrRequestConfig_headers = Map.fromList hs
245 , _xhrRequestConfig_user = Nothing
258 , _xhrRequestConfig_user = Nothing
246 , _xhrRequestConfig_password = Nothing
259 , _xhrRequestConfig_password = Nothing
247− , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
260+ , _xhrRequestConfig_responseType = Nothing
248− , _xhrRequestConfig_sendData = mempty
249 , _xhrRequestConfig_withCredentials = False
261 , _xhrRequestConfig_withCredentials = False
262+ , _xhrRequestConfig_sendData = Nothing
251 Just rBody -> liftA2 mkConfigBody xhrHeaders rBody
264 Just rBody -> liftA2 mkConfigBody xhrHeaders rBody
@@ -254,16 +267,16 @@ reqToReflexRequest reqMeth reqHost req =
254 mkAuth _ (Left e) = Left e
267 mkAuth _ (Left e) = Left e
255 mkAuth Nothing r = r
268 mkAuth Nothing r = r
256 mkAuth (Just (BasicAuthData u p)) (Right config) = Right $ config
269 mkAuth (Just (BasicAuthData u p)) (Right config) = Right $ config
257− { _xhrRequestConfig_user = Just $ TE.decodeUtf8 u
270+ { _xhrRequestConfig_user = Just $ T.decodeUtf8 u
258− , _xhrRequestConfig_password = Just $ TE.decodeUtf8 p}
271+ , _xhrRequestConfig_password = Just $ T.decodeUtf8 p}
260 addAuth :: Dynamic t (Either Text (XhrRequestConfig x))
273 addAuth :: Dynamic t (Either Text (XhrRequestConfig x))
261 -> Dynamic t (Either Text (XhrRequestConfig x))
274 -> Dynamic t (Either Text (XhrRequestConfig x))
262 addAuth xhr = case authData req of
275 addAuth xhr = case authData req of
263− Nothing -> xhr
276+ Nothing -> xhr
264 Just auth -> liftA2 mkAuth auth xhr
277 Just auth -> liftA2 mkAuth auth xhr
266− xhrReq = (liftA2 . liftA2) (\p opt -> XhrRequest reqMeth p opt) xhrUrl (addAuth xhrOpts)
279+ xhrReq = (liftA2 . liftA2) (\p opt -> SomeXhrRequest $ XhrRequest reqMeth p opt) xhrUrl (addAuth xhrOpts)
268 in xhrReq
281 in xhrReq
@@ -272,8 +285,14 @@ reqToReflexRequest reqMeth reqHost req =
272displayHttpRequest :: Text -> Text
285displayHttpRequest :: Text -> Text
273displayHttpRequest httpmethod = "HTTP " <> httpmethod <> " request"
286displayHttpRequest httpmethod = "HTTP " <> httpmethod <> " request"
288+instance IsXhrPayload o => IsXhrPayload (Maybe o) where
289+ sendXhrPayload xhr = maybe (sendXhrPayload xhr ()) (sendXhrPayload xhr)
291+instance (IsXhrPayload o, IsXhrPayload o') => IsXhrPayload (Either o o') where
292+ sendXhrPayload xhr = either (sendXhrPayload xhr) (sendXhrPayload xhr)
275-- | This function performs the request
294-- | This function performs the request
276−performRequests :: forall t m f tag.(SupportsServantReflex t m, Traversable f)
295+performRequests :: forall t m f tag. (SupportsServantReflex t m, Traversable f)
278 -> Dynamic t (f (Req t))
297 -> Dynamic t (f (Req t))
279 -> Dynamic t BaseUrl
298 -> Dynamic t BaseUrl
@@ -286,6 +305,7 @@ performRequests reqMeth rs reqHost opts trigger = do
286 reqToReflexRequest reqMeth reqHost <$> fxhr) <$> rs
305 reqToReflexRequest reqMeth reqHost <$> fxhr) <$> rs
288 -- xhrReqs = fmap snd <$> xhrReqsAndDebugs
307 -- xhrReqs = fmap snd <$> xhrReqsAndDebugs
308+ reqs :: Event t (Compose ((,) tag) f (Either Text SomeXhrRequest))
289 reqs = attachPromptlyDynWith
309 reqs = attachPromptlyDynWith
290 (\fxhr t -> Compose (t, fxhr)) xhrReqs trigger
310 (\fxhr t -> Compose (t, fxhr)) xhrReqs trigger
@@ -301,32 +321,31 @@ performSomeRequestsAsync
301 HasWebView (Performable m),
321 HasWebView (Performable m),
302 PerformEvent t m,
322 PerformEvent t m,
303 TriggerEvent t m,
323 TriggerEvent t m,
304− Traversable f,
324+ Traversable f
308 => ClientOptions
326 => ClientOptions
309− -> Event t (f (Either Text (XhrRequest a)))
327+ -> Event t (f (Either Text SomeXhrRequest))
310 -> m (Event t (f (Either Text XhrResponse)))
328 -> m (Event t (f (Either Text XhrResponse)))
311−performSomeRequestsAsync opts =
329+performSomeRequestsAsync opts sxhr =
312− performSomeRequestsAsync' opts newXMLHttpRequest . fmap return
330+ performSomeRequestsAsync' opts newXMLHttpRequest $ return <$> sxhr
315------------------------------------------------------------------------------
333------------------------------------------------------------------------------
316-- | A modified version or Reflex.Dom.Xhr.performRequestsAsync
334-- | A modified version or Reflex.Dom.Xhr.performRequestsAsync
317-- that accepts 'f (Either e (XhrRequestb))' events
335-- that accepts 'f (Either e (XhrRequestb))' events
318performSomeRequestsAsync'
336performSomeRequestsAsync'
319− :: (MonadJSM (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f, Show b)
337+ :: (MonadJSM (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f)
320 => ClientOptions
338 => ClientOptions
321− -> (XhrRequest b -> (a -> JSM ()) -> Performable m XMLHttpRequest)
339+ -> (forall b. IsXhrPayload b => XhrRequest b -> (a -> JSM ()) -> Performable m XMLHttpRequest)
322− -> Event t (Performable m (f (Either Text (XhrRequest b)))) -> m (Event t (f (Either Text a)))
340+ -> Event t (Performable m (f (Either Text SomeXhrRequest)))
341+ -> m (Event t (f (Either Text a)))
323performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
342performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
324 rs <- hrs
343 rs <- hrs
325 resps <- forM rs $ \r -> case r of
344 resps <- forM rs $ \r -> case r of
326 Left e -> do
345 Left e -> do
327 resp <- liftIO $ newMVar (Left e)
346 resp <- liftIO $ newMVar (Left e)
328 return resp
347 return resp
329− Right r' -> do
348+ Right (SomeXhrRequest r') -> do
330 resp <- liftIO newEmptyMVar
349 resp <- liftIO newEmptyMVar
331 r'' <- liftJSM $ (optsRequestFixup opts) r'
350 r'' <- liftJSM $ (optsRequestFixup opts) r'
332 _ <- newXhr r'' $ liftIO . putMVar resp . Right
351 _ <- newXhr r'' $ liftIO . putMVar resp . Right
@@ -335,12 +354,6 @@ performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs
335 return ()
354 return ()
339−type XhrPayload = ByteString
340−bytesToPayload :: BL.ByteString -> XhrPayload
341−bytesToPayload = BL.toStrict
344performRequestsCT
357performRequestsCT
345 :: (SupportsServantReflex t m,
358 :: (SupportsServantReflex t m,
346 MimeUnrender ct a, Traversable f)
359 MimeUnrender ct a, Traversable f)
@@ -353,15 +366,14 @@ performRequestsCT
353 -> m (Event t (f (ReqResult tag a)))
366 -> m (Event t (f (ReqResult tag a)))
354performRequestsCT ct reqMeth reqs reqHost opts trigger = do
367performRequestsCT ct reqMeth reqs reqHost opts trigger = do
355 resps <- performRequests reqMeth reqs reqHost opts trigger
368 resps <- performRequests reqMeth reqs reqHost opts trigger
356− let decodeResp (Just (XhrResponseBody_ArrayBuffer x)) =
357− first T.pack .
369+ let decodeResp x = first T.pack
358− mimeUnrender ct $
370+ . mimeUnrender ct
359− BL.fromStrict x
371+ . JS.textToJSString
360− decodeResp _ = Left "No body"
372+ =<< note "No body text" (_xhrResponse_responseText x)
361 return $ fmap
373 return $ fmap
362 (\(t,rs) -> ffor rs $ \r -> case r of
374 (\(t,rs) -> ffor rs $ \r -> case r of
363 Left e -> RequestFailure t e
375 Left e -> RequestFailure t e
364− Right g -> evalResponse (decodeResp . _xhrResponse_response) (t,g)
376+ Right g -> evalResponse decodeResp (t,g)
@@ -388,7 +400,7 @@ evalResponse
388 :: (XhrResponse -> Either Text a)
400 :: (XhrResponse -> Either Text a)
389 -> (tag, XhrResponse)
401 -> (tag, XhrResponse)
390 -> ReqResult tag a
402 -> ReqResult tag a
391−evalResponse decode (tag, xhr) =
403+evalResponse decodeRes (tag, xhr) =
392 let status = _xhrResponse_status xhr
404 let status = _xhrResponse_status xhr
393 okStatus = status >= 200 && status < 400
405 okStatus = status >= 200 && status < 400
394 errMsg = fromMaybe
406 errMsg = fromMaybe
@@ -399,33 +411,56 @@ evalResponse decode (tag, xhr) =
399 then either
411 then either
400 (\e -> ResponseFailure tag e xhr)
412 (\e -> ResponseFailure tag e xhr)
401 (\v -> ResponseSuccess tag v xhr)
413 (\v -> ResponseSuccess tag v xhr)
402− (decode xhr)
414+ (decodeRes xhr)
403 else ResponseFailure tag errMsg xhr
415 else ResponseFailure tag errMsg xhr
404 in respPayld
416 in respPayld
406−------------------------------------------------------------------------------
407−-- | Utility for simultaneously accessing/filtering Success and Failure
408−-- response 'Event's,
409−fanReqResult :: Reflex t => Event t (ReqResult tag a) -> (Event t Text, Event t a)
410−fanReqResult = fmapMaybe reqFailure &&& fmapMaybe reqSuccess
412−------------------------------------------------------------------------------
419+-- | Similar to 'Servant.API.ContentType.MimeUnrender' but with the differnce that
413−-- | Utility for simultaneously accessing/filtering Success and Failure
420+-- the second argument expects a JSString instead of ByteString.
414−-- response 'Event's, but keeping the request tag
421+-- This can lead to better performance ghcjs.
415−fanReqResult' :: Reflex t => Event t (ReqResult tag a) -> (Event t (tag, Text), Event t (tag, a))
422+class Accept contentType => MimeUnrender contentType a where
416−fanReqResult' = fmapMaybe reqFailure' &&& fmapMaybe reqSuccess'
423+ mimeUnrender :: Proxy contentType -> JS.JSString -> Either String a
425+instance FromJSON a => MimeUnrender JSON a where
426+ mimeUnrender _ = note "Failed to decode response" . jsonDecode
428+-- | @urlDecodeAsForm@
429+-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
430+-- holds if every element of x is non-null (i.e., not @("", "")@)
431+instance FromForm a => MimeUnrender FormUrlEncoded a where
432+ mimeUnrender _ = first T.unpack . urlDecodeAsForm . BB.toLazyByteString . T.encodeUtf8Builder . JS.textFromJSString
434+-- | @left show . TextL.decodeUtf8'@
435+instance MimeUnrender PlainText TL.Text where
436+ mimeUnrender _ = pure . JS.lazyTextFromJSString
438+-- | @left show . TextS.decodeUtf8' . toStrict@
439+instance MimeUnrender PlainText T.Text where
440+ mimeUnrender _ = pure . JS.textFromJSString
442+-- | @Right . BC.unpack@
443+instance MimeUnrender PlainText String where
444+ mimeUnrender _ = Right . T.unpack . JS.textFromJSString
447+instance MimeUnrender OctetStream ByteString where
448+ mimeUnrender _ = pure . BB.toLazyByteString . T.encodeUtf8Builder . JS.textFromJSString
450+-- | @Right . toStrict@
451+instance MimeUnrender OctetStream BS.ByteString where
452+ mimeUnrender _ = pure . T.encodeUtf8 . JS.textFromJSString
419note :: e -> Maybe a -> Either e a
454note :: e -> Maybe a -> Either e a
420−note e = maybe (Left e) Right
455+note e Nothing = Left e
456+note _ (Just a) = Right a
422fmapL :: (e -> e') -> Either e a -> Either e' a
458fmapL :: (e -> e') -> Either e a -> Either e' a
423fmapL _ (Right a) = Right a
459fmapL _ (Right a) = Right a
424fmapL f (Left e) = Left (f e)
460fmapL f (Left e) = Left (f e)
426−builderToText :: Builder.Builder -> T.Text
462+builderToText :: BB.Builder -> T.Text
427−builderToText = TE.decodeUtf8 . BL.toStrict . Builder.toLazyByteString
463+builderToText = T.decodeUtf8 . BL.toStrict . BB.toLazyByteString
429escape :: T.Text -> T.Text
465escape :: T.Text -> T.Text
430−escape = T.pack . N.escapeURIString (not . N.isReserved) . T.unpack . TE.decodeUtf8 . BL.toStrict . Builder.toLazyByteString . toEncodedUrlPiece
466+escape = T.pack . N.escapeURIString (not . N.isReserved) . T.unpack . T.decodeUtf8 . BL.toStrict . BB.toLazyByteString . toEncodedUrlPiece