@@ -11,31 +11,27 @@ import qualified Data.ByteString as BS
11
11
import qualified Data.ByteString.Lazy as BSL
12
12
import qualified Data.Text as T
13
13
import qualified Data.Text.Lazy as TL
14
- import qualified Data.Text.IO as T
15
- import qualified Data.Text.Encoding as T
16
- import qualified Data.Text.Lazy.Encoding as TL
17
- import qualified Data.Text.Lazy.Builder as TLB
14
+ import qualified Data.Text.Encoding as TE
15
+ import qualified Data.Text.Lazy.Encoding as TLE
18
16
import qualified Data.Map as Map
19
17
import qualified Data.HashMap.Strict as HashMap
20
18
import qualified Data.List as List
19
+ import Data.Maybe (fromMaybe )
21
20
import GHC.Word (Word64 )
22
21
23
22
import qualified System.Directory as Dir
24
- import System.FilePath as FP
23
+ import System.FilePath ( (</>) , takeExtension )
25
24
import Control.Applicative ((<|>) )
26
25
import Control.Arrow ((***) )
27
- import Control.Concurrent.STM (atomically , newTVarIO , readTVar , writeTVar , TVar )
28
- import Control.Exception (finally , throw )
29
- import Language.Haskell.TH ( runIO )
26
+ import Control.Concurrent.STM (atomically , newTVarIO , readTVar , readTVarIO , writeTVar , TVar )
27
+ import Control.Exception (finally )
28
+ import qualified Language.Haskell.TH as TH
30
29
import Data.FileEmbed (bsToExp )
31
30
import qualified Data.Aeson.Encoding as A
32
31
33
32
import Snap.Core hiding (path , headers )
34
- import qualified Data.CaseInsensitive as CI (original , mk )
35
- import qualified Data.Bifunctor (first )
33
+ import qualified Data.CaseInsensitive as CI
36
34
37
- import qualified Develop.Generate.Help as Generate
38
- import qualified Develop.StaticFiles as StaticFiles
39
35
import qualified Json.Decode as D
40
36
import qualified Json.Encode as E
41
37
import qualified Json.String
@@ -45,19 +41,15 @@ import Lamdera
45
41
import qualified Data.UUID as UUID
46
42
import qualified Data.UUID.V4 as UUID
47
43
import BroadcastChan
48
- import Control.Timeout
49
- import Data.Time.Clock (getCurrentTime , diffUTCTime )
50
- import qualified Network.WebSockets as WS
44
+ import Control.Timeout (timeout )
51
45
import qualified Network.WebSockets.Snap as WS
52
46
import SocketServer
53
- import Data.Word (Word8 )
54
- import System.Process
55
47
56
- import System.Entropy
48
+ import System.Entropy ( getEntropy )
57
49
import Snap.Util.FileServe (
58
- getSafePath , serveDirectoryWith , defaultDirectoryConfig , defaultMimeTypes , mimeTypes , MimeMap , DirectoryConfig
50
+ getSafePath , serveDirectoryWith , defaultDirectoryConfig , defaultMimeTypes , mimeTypes , DirectoryConfig
59
51
)
60
- import Control.Monad (guard , void )
52
+ import Control.Monad (guard )
61
53
62
54
import qualified Lamdera.CLI.Check
63
55
import qualified Lamdera.Relative
@@ -91,7 +83,7 @@ withEnd (mClients, mLeader, mChan, beState) io = do
91
83
let
92
84
end = do
93
85
debug " [backendSt] 🧠"
94
- text <- atomically $ readTVar beState
86
+ text <- readTVarIO beState
95
87
bePath <- lamderaBackendDevSnapshotPath
96
88
writeUtf8 bePath text
97
89
@@ -133,10 +125,7 @@ serveUnmatchedUrlsToIndex root serveElm =
133
125
134
126
prepareLocalDev :: FilePath -> IO FilePath
135
127
prepareLocalDev root = do
136
- overrideM <- Lamdera.Relative. readFile " extra/LocalDev/LocalDev.elm"
137
- let
138
- cache = lamderaCache root
139
- harnessPath = cache </> " LocalDev.elm"
128
+ overrideM <- Lamdera.Relative. readDir TE. decodeUtf8 " extra/LocalDev/runtime-src"
140
129
141
130
-- This needs to be moved to an on-demand action, as it has to query production and
142
131
-- thus isn't appropriate to run on every single recompile
@@ -145,22 +134,23 @@ prepareLocalDev root = do
145
134
146
135
rpcExists <- doesFileExist $ root </> " src" </> " RPC.elm"
147
136
148
- case overrideM of
149
- Just override -> do
150
- writeIfDifferent harnessPath
151
- (override
152
- & replaceVersionMarker
153
- & replaceRpcMarker rpcExists
154
- )
137
+ let
138
+ cache = lamderaCache root
139
+ harnessPath = " LocalDev.elm"
140
+
141
+ patchedContent path content =
142
+ if path == harnessPath
143
+ then content & replaceVersionMarker & replaceRpcMarker rpcExists
144
+ else content
145
+
146
+ processFile (path, content) =
147
+ writeIfDifferent (cache </> path) $ patchedContent path content
148
+
149
+ files = fromMaybe lamderaLocalDevDir overrideM
155
150
156
- Nothing ->
157
- writeIfDifferent harnessPath
158
- (lamderaLocalDev
159
- & replaceVersionMarker
160
- & replaceRpcMarker rpcExists
161
- )
151
+ mapM_ processFile files
162
152
163
- pure harnessPath
153
+ pure $ cache </> harnessPath
164
154
165
155
166
156
replaceVersionMarker :: Text -> Text
@@ -205,9 +195,13 @@ replaceRpcMarker shouldReplace localdev =
205
195
\ {-}"
206
196
207
197
208
- lamderaLocalDev :: Text
209
- lamderaLocalDev =
210
- T. decodeUtf8 $ (bsToExp =<< runIO (Lamdera.Relative. readByteString " extra/LocalDev/LocalDev.elm" ))
198
+ lamderaLocalDevDir :: [(FilePath , Text )]
199
+ lamderaLocalDevDir =
200
+ $ (do
201
+ bsPairs <- TH. runIO (Lamdera.Relative. readDir id " extra/LocalDev/runtime-src" )
202
+ let toTuple (fp, bs) = [| (fp, TE. decodeUtf8 $ (bsToExp bs)) | ]
203
+ TH. ListE <$> mapM toTuple (fromMaybe [] bsPairs)
204
+ )
211
205
212
206
213
207
refreshClients (mClients, mLeader, mChan, beState) =
@@ -229,10 +223,10 @@ serveWebsocket root (mClients, mLeader, mChan, beState) =
229
223
let cookie = Cookie " sid" newSid Nothing Nothing Nothing False False
230
224
modifyResponse $ addResponseCookie cookie
231
225
232
- pure $ T . decodeUtf8 $ newSid
226
+ pure $ TE . decodeUtf8 $ newSid
233
227
234
228
Just sid_ ->
235
- pure $ T . decodeUtf8 $ cookieValue sid_
229
+ pure $ TE . decodeUtf8 $ cookieValue sid_
236
230
237
231
case mKey of
238
232
Just key -> do
@@ -252,14 +246,14 @@ serveWebsocket root (mClients, mLeader, mChan, beState) =
252
246
onlyWhen leaderChanged $ do
253
247
sendToLeader mClients mLeader (\ leader -> do
254
248
-- Tell the new leader about the backend state they need
255
- atomically $ readTVar beState
249
+ readTVarIO beState
256
250
)
257
251
-- Tell everyone about the new leader (also causes actual leader to go active as leader)
258
252
broadcastLeader mClients mLeader
259
253
260
254
SocketServer. broadcastImpl mClients $ " {\" t\" :\" c\" ,\" s\" :\" " <> sessionId <> " \" ,\" c\" :\" " <> clientId <> " \" }"
261
255
262
- leader <- atomically $ readTVar mLeader
256
+ leader <- readTVarIO mLeader
263
257
case leader of
264
258
Just leaderId ->
265
259
pure $ Just $ " {\" t\" :\" s\" ,\" c\" :\" " <> clientId <> " \" ,\" l\" :\" " <> leaderId <> " \" }"
@@ -308,7 +302,7 @@ serveWebsocket root (mClients, mLeader, mChan, beState) =
308
302
SocketServer. broadcastImpl mClients text
309
303
310
304
WS. runWebSocketsSnap $
311
- SocketServer. socketHandler mClients mLeader beState onJoined onReceive (T . decodeUtf8 key) sessionId
305
+ SocketServer. socketHandler mClients mLeader beState onJoined onReceive (TE . decodeUtf8 key) sessionId
312
306
313
307
Nothing ->
314
308
error404 " missing sec-websocket-key header"
@@ -399,9 +393,9 @@ serveExperimentalWrite root path = do
399
393
Just " image/jpeg" -> Lamdera. writeBinary fullpath rbody
400
394
401
395
_ ->
402
- writeIfDifferent fullpath (TL. toStrict $ TL . decodeUtf8 rbody)
396
+ writeIfDifferent fullpath (TL. toStrict $ TLE . decodeUtf8 rbody)
403
397
404
- jsonResponse $ B. byteString $ " { written: '" <> T . encodeUtf8 (T. pack fullpath) <> " '}"
398
+ jsonResponse $ B. byteString $ " { written: '" <> TE . encodeUtf8 (T. pack fullpath) <> " '}"
405
399
406
400
407
401
serveExperimentalList :: FilePath -> Text -> Snap ()
@@ -543,10 +537,10 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
543
537
let cookie = Cookie " sid" newSid Nothing Nothing Nothing False False
544
538
modifyResponse $ addResponseCookie cookie
545
539
546
- pure $ T . decodeUtf8 $ newSid
540
+ pure $ TE . decodeUtf8 $ newSid
547
541
548
542
Just sid_ ->
549
- pure $ T . decodeUtf8 $ cookieValue sid_
543
+ pure $ TE . decodeUtf8 $ cookieValue sid_
550
544
551
545
onlyWhen (mEndpoint == Nothing ) $ error500 " no endpoint present"
552
546
@@ -570,10 +564,10 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
570
564
-- Unfortunately the JSON string encoding logic is hidden inside Data.Aeson.Encoding.Internal
571
565
-- so off we go with all the silly format hops
572
566
escapeJsonString :: Text -> Text
573
- escapeJsonString t = A. text t & A. encodingToLazyByteString & BSL. toStrict & T . decodeUtf8
567
+ escapeJsonString t = A. text t & A. encodingToLazyByteString & BSL. toStrict & TE . decodeUtf8
574
568
575
569
escapedBody =
576
- rbody & TL . decodeUtf8 & TL. toStrict & escapeText
570
+ rbody & TLE . decodeUtf8 & TL. toStrict & escapeText
577
571
578
572
escapeText :: Text -> E. Value
579
573
escapeText t =
@@ -588,14 +582,14 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
588
582
E. object
589
583
[ (" t" , E. string " q" )
590
584
, (" s" , E. text sid)
591
- , (" e" , E. text $ T . decodeUtf8 endpoint)
585
+ , (" e" , E. text $ TE . decodeUtf8 endpoint)
592
586
, (" r" , E. text reqId)
593
587
, (" h" , E. String $ Ext.Common. textToBuilder $ encodeToText requestHeadersJson)
594
588
, value
595
589
]
596
590
& encodeToText
597
591
598
- encodeToText encoder = encoder & E. encode & B. toLazyByteString & BSL. toStrict & T . decodeUtf8
592
+ encodeToText encoder = encoder & E. encode & B. toLazyByteString & BSL. toStrict & TE . decodeUtf8
599
593
600
594
requestPayload =
601
595
case contentType of
@@ -619,14 +613,14 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
619
613
values =
620
614
case vals of
621
615
[] -> " null"
622
- val: [] -> T. concat [" \" " , (T . decodeUtf8 val & escapeJsonString), " \" " ]
616
+ val: [] -> T. concat [" \" " , (TE . decodeUtf8 val & escapeJsonString), " \" " ]
623
617
_ ->
624
618
vals
625
- & fmap (\ v -> T. concat [" \" " , (T . decodeUtf8 v & escapeJsonString), " \" " ])
619
+ & fmap (\ v -> T. concat [" \" " , (TE . decodeUtf8 v & escapeJsonString), " \" " ])
626
620
& T. intercalate " ,"
627
621
& (\ v -> T. concat [" [" , v, " ]" ])
628
622
in
629
- T. concat [" \" " , T . decodeUtf8 key, " \" :" , values]
623
+ T. concat [" \" " , TE . decodeUtf8 key, " \" :" , values]
630
624
)
631
625
& (\ v -> T. concat [" {" , (v & T. intercalate " ," ), " }" ])
632
626
in
@@ -648,7 +642,7 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
648
642
| otherwise -> loopRead
649
643
Nothing -> loopRead
650
644
651
- leader <- liftIO $ atomically $ readTVar mLeader
645
+ leader <- liftIO $ readTVarIO mLeader
652
646
case leader of
653
647
Just leaderId -> do
654
648
liftIO $ sendToLeader mClients mLeader (\ leader_ -> pure requestPayload)
@@ -672,12 +666,12 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
672
666
])
673
667
674
668
decodeResult =
675
- D. fromByteString decoder (T . encodeUtf8 chanText)
669
+ D. fromByteString decoder (TE . encodeUtf8 chanText)
676
670
677
671
case decodeResult of
678
672
Right (statusCode, statusText, headers, (bodyType, bodyEncoded)) -> do
679
673
680
- let response = TL. toStrict $ TL . decodeUtf8 $ B. toLazyByteString bodyEncoded
674
+ let response = TL. toStrict $ TLE . decodeUtf8 $ B. toLazyByteString bodyEncoded
681
675
debugT $ " RPC:↙️ response:" <> response
682
676
debug $ show (statusCode, statusText)
683
677
onlyWhen (bodyType == " i" ) (modifyResponse $ setContentType " application/octet-stream" )
@@ -694,12 +688,12 @@ serveRpc (mClients, mLeader, mChan, beState) port = do
694
688
695
689
Left jsonProblem -> do
696
690
debugT $ " 😢 rpc response decoding failed: " <> show_ jsonProblem <> " \n " <> chanText
697
- writeBuilder $ B. byteString $ " rpc response decoding failed for " <> T . encodeUtf8 chanText
691
+ writeBuilder $ B. byteString $ " rpc response decoding failed for " <> TE . encodeUtf8 chanText
698
692
699
693
700
694
Nothing -> do
701
695
debugT $ " ⏰ RPC timed out for:" <> requestPayload
702
- writeBuilder $ B. byteString $ T . encodeUtf8 $ " error:timeout:" <> show_ seconds <> " s"
696
+ writeBuilder $ B. byteString $ TE . encodeUtf8 $ " error:timeout:" <> show_ seconds <> " s"
703
697
704
698
705
699
Nothing -> do
@@ -721,7 +715,7 @@ _10MB =
721
715
logger :: BS. ByteString -> IO ()
722
716
logger =
723
717
(\ bs ->
724
- atomicPutStrLn $ T. unpack $ T . decodeUtf8 bs
718
+ atomicPutStrLn $ T. unpack $ TE . decodeUtf8 bs
725
719
)
726
720
727
721
jsonResponse :: B. Builder -> Snap ()
0 commit comments