@@ -8,12 +8,11 @@ open Suave
88open Suave.Filters
99open Suave.Operators
1010open Suave.Sockets
11- open Suave.Sockets .Control
12- open Suave.Utils
13- open Suave.WebSocket
1411open System
1512open System.Net
13+ open System.Net .Sockets
1614open System.Threading
15+ open System.Threading .Tasks
1716
1817let port =
1918 let rec findPort port =
@@ -25,14 +24,16 @@ let port =
2524
2625 findPort 8080 us
2726
27+ [<RequireQualifiedAccess>]
28+ [<Struct>]
2829type BuildEvent =
2930 | BuildFable
3031 | BuildMd
3132 | BuildStyle
3233 | Noop
3334
34- let ( handleWatcherEvents : FileChange seq -> unit ), socketHandler =
35- let refreshEvent = new Event<_ >()
35+ let ( handleWatcherEvents : FileChange seq -> unit ), sseHandler =
36+ let refreshEvent = new Event< unit >()
3637
3738 let buildFable () =
3839 let cmd = " fable src"
@@ -59,24 +60,26 @@ let (handleWatcherEvents: FileChange seq -> unit), socketHandler =
5960 |> Seq.map ( fun e ->
6061 let fi = FileInfo.ofPath e.FullPath
6162
62- Trace.traceImportant $" %s {fi.FullName} was changed."
63+ Trace.traceImportant $" %s {fi.FullName} was changed. ext: %s {fi.Extension} "
6364
64- match fi.FullName with
65- | x when x.EndsWith( " .fs" ) -> BuildFable
66- | x when x.EndsWith( " .md" ) || x.EndsWith( " .yml" ) || x.EndsWith( " .yaml" ) -> BuildMd
67- | x when x.EndsWith( " .scss" ) -> BuildStyle
68- | _ -> Noop)
65+ match fi.Extension with
66+ | " .fs" -> BuildEvent.BuildFable
67+ | " .md"
68+ | " .yml"
69+ | " .yaml" -> BuildEvent.BuildMd
70+ | " .scss" -> BuildEvent.BuildStyle
71+ | _ -> BuildEvent.Noop)
6972 |> Set.ofSeq
7073
7174 let fableOrMd =
72- match [ BuildFable; BuildMd ] |> List.map es.Contains with
75+ match [ BuildEvent. BuildFable; BuildEvent. BuildMd ] |> List.map es.Contains with
7376 | [ true ; true ] -> buildFable ()
7477 | [ _; true ] -> buildMd ()
7578 | [ true ; _ ] -> buildFable ()
7679 | _ -> Ok false
7780
7881 let style =
79- match es |> Set.contains BuildStyle with
82+ match es |> Set.contains BuildEvent. BuildStyle with
8083 | true -> buildStyle ()
8184 | _ -> Ok false
8285
@@ -87,39 +90,33 @@ let (handleWatcherEvents: FileChange seq -> unit), socketHandler =
8790 printfn " refresh event is triggered."
8891 | _ -> printfn " refresh event not triggered."
8992
90- let socketHandler ( ws : WebSocket ) _ =
91- let rec refreshLoop ( ct : CancellationToken ) =
92- task {
93- ct.ThrowIfCancellationRequested()
94- do ! refreshEvent.Publish |> Async.AwaitEvent
95-
96- printfn " refresh client."
97- let seg = ASCII.bytes " refreshed" |> ByteSegment
98- let! _ = ws.send Text seg true
99-
100- return ! refreshLoop ct
101- }
102-
103- let rec mainLoop ( cts : CancellationTokenSource ) =
104- socket {
105- let! msg = ws.read ()
106-
107- match msg with
108- | Close, _, _ ->
109- // use _ = cts
110- cts.Cancel()
111-
112- let emptyResponse = [||] |> ByteSegment
113- do ! ws.send Close emptyResponse true
114- printfn " WebSocket connection closed gracefully."
115- | _ -> return ! mainLoop cts
116- }
117-
118- let cts = new CancellationTokenSource()
119- refreshLoop cts.Token |> ignore
120- mainLoop cts
121-
122- handleWatcherEvents, socketHandler
93+ let sseHandler ( conn : Connection ) : Task < unit > =
94+ task {
95+ try
96+ // NOTE: Tell the browser how long to wait before retrying the connection.
97+ do ! EventSource.retry conn 1000 u
98+
99+ // NOTE: Initial event so the client can confirm it is connected.
100+ do ! EventSource.eventType conn " connected"
101+ do ! EventSource.data conn " ok"
102+ do ! EventSource.dispatch conn
103+
104+ while true do
105+ do !
106+ Async.StartAsTask(
107+ refreshEvent.Publish |> Async.AwaitEvent,
108+ cancellationToken = CancellationToken.None
109+ )
110+
111+ do ! EventSource.eventType conn " refresh"
112+ do ! EventSource.data conn " refreshed"
113+ do ! EventSource.dispatch conn
114+ with
115+ | : ? OperationCanceledException -> ()
116+ | : ? SocketException -> ()
117+ }
118+
119+ handleWatcherEvents, sseHandler
123120
124121let suaveConfig ( home : string ) ( ct : CancellationToken ) =
125122 let home = IO.Path.GetFullPath home
@@ -149,7 +146,7 @@ let webpart (root: string) : WebPart =
149146
150147 choose [
151148
152- path " /websocket " >=> handShake socketHandler
149+ path " /sse " >=> EventSource. handShake sseHandler
153150
154151 GET
155152 >=> Writers.setHeader " Cache-Control" " no-cache, no-store, must-revalidate"
0 commit comments