on master help sign in
Fix timeout handling with custom task awaiter and standardize timestamps to milliseconds
9d5373aiko
client/ApiClient.fs+15−4
@@ -4,9 +4,20 @@ open System
4open System.Net.Http
4open System.Net.Http
5open System.Text
5open System.Text
6open System.Text.Json
6open System.Text.Json
7+open System.Threading.Tasks
7
8
8module ApiClient =
9module ApiClient =
9
10
11+ /// Awaits a Task without converting TaskCanceledException to F# async
12+ /// cancellation (which bypasses try...with). Re-raises it as a regular exception.
13+ let private awaitTask (t: Task<'T>) : Async<'T> =
14+ Async.FromContinuations(fun (ok, err, _cancel) ->
15+ t.ContinueWith(fun (t: Task<'T>) ->
16+ if t.IsFaulted then err t.Exception.InnerException
17+ elif t.IsCanceled then err (TimeoutException("Request timed out"))
18+ else ok t.Result)
19+ |> ignore)
20+
10 let private client =
21 let private client =
11 let c = new HttpClient()
22 let c = new HttpClient()
12 c.Timeout <- TimeSpan.FromSeconds(60.)
23 c.Timeout <- TimeSpan.FromSeconds(60.)
@@ -87,13 +98,13 @@ module ApiClient =
87 request.Content <- new StringContent($"""{{"cursor":{cursor},"timeout":30000}}""", Encoding.UTF8, "application/json")
98 request.Content <- new StringContent($"""{{"cursor":{cursor},"timeout":30000}}""", Encoding.UTF8, "application/json")
88 request.Headers.Add("Authorization", $"Bearer {token}")
99 request.Headers.Add("Authorization", $"Bearer {token}")
89
100
90− let! response = pollClient.SendAsync(request) |> Async.AwaitTask
101+ let! response = pollClient.SendAsync(request) |> awaitTask
91− let! text = response.Content.ReadAsStringAsync() |> Async.AwaitTask
102+ let! text = response.Content.ReadAsStringAsync() |> awaitTask
92 let doc = JsonDocument.Parse(text)
103 let doc = JsonDocument.Parse(text)
93 let root = expectObject doc "/poll"
104 let root = expectObject doc "/poll"
94− let cursor = root.GetProperty("cursor").GetUInt64
105+ let cursor = root.GetProperty("cursor").GetUInt64()
95
106
96− let events= root.GetProperty("events")
107+ let events = root.GetProperty("events")
97
108
98 let events =
109 let events =
99 [ for e in events.EnumerateArray() do
110 [ for e in events.EnumerateArray() do
client/App.fs+11−18
@@ -48,8 +48,7 @@ module App =
48 NewNickname: string
48 NewNickname: string
49 Error: string option
49 Error: string option
50 PollCursor: uint64
50 PollCursor: uint64
51− LastSendStatus: string
51+ LastSendStatus: string }
52− Events: list<JsonElement> }
53
52
54 // ── Msg ──
53 // ── Msg ──
55
54
@@ -69,7 +68,7 @@ module App =
69 | SetNewNickname of string
68 | SetNewNickname of string
70 | DoSaveContact
69 | DoSaveContact
71 | DoDeleteContact of string
70 | DoDeleteContact of string
72− | PollResult of (string * string * uint64 * string) list * string * uint64 * list<JsonElement>
71+ | PollResult of (string * string * int64 * string) list * string * uint64
73 | DoPoll
72 | DoPoll
74 | CopyPubKey
73 | CopyPubKey
75 | DismissError
74 | DismissError
@@ -117,8 +116,7 @@ module App =
117 NewNickname = ""
116 NewNickname = ""
118 Error = None
117 Error = None
119 PollCursor = 0UL
118 PollCursor = 0UL
120− LastSendStatus = ""
119+ LastSendStatus = "" },
121− Events = [] },
122 []
120 []
123
121
124 // ── Update ──
122 // ── Update ──
@@ -176,7 +174,7 @@ module App =
176
174
177 | Sent(_, status) -> { model with LastSendStatus = status }, []
175 | Sent(_, status) -> { model with LastSendStatus = status }, []
178
176
179− | PollResult(incoming, status, newCursor, events) ->
177+ | PollResult(incoming, status, newCursor) ->
180 let model' =
178 let model' =
181 incoming
179 incoming
182 |> List.fold
180 |> List.fold
@@ -201,7 +199,7 @@ module App =
201 { m with
199 { m with
202 Contacts = contacts
200 Contacts = contacts
203 Messages = m.Messages |> Map.add fromPk (msgs @ [ newMsg ]) })
201 Messages = m.Messages |> Map.add fromPk (msgs @ [ newMsg ]) })
204− { model with PollStatus = status; PollCursor = newCursor; Events = model.Events @ events }
202+ { model with PollStatus = status; PollCursor = newCursor }
205
203
206 match model'.Conn with
204 match model'.Conn with
207 | Online token -> model', [ CmdPoll(model'.ServerUrl, token, model'.PrivKey.Value, model'.PollCursor) ]
205 | Online token -> model', [ CmdPoll(model'.ServerUrl, token, model'.PrivKey.Value, model'.PollCursor) ]
@@ -315,9 +313,9 @@ module App =
315 let tsEl = payload.GetProperty("timestamp")
313 let tsEl = payload.GetProperty("timestamp")
316 let ts =
314 let ts =
317 if tsEl.ValueKind = JsonValueKind.String then
315 if tsEl.ValueKind = JsonValueKind.String then
318− UInt64.Parse(tsEl.GetString())
316+ Int64.Parse(tsEl.GetString())
319 else
317 else
320− tsEl.GetUInt64()
318+ tsEl.GetInt64()
321 let blob = Crypto.fromHex blobHex
319 let blob = Crypto.fromHex blobHex
322
320
323 match Crypto.decrypt privKey blob with
321 match Crypto.decrypt privKey blob with
@@ -345,15 +343,14 @@ module App =
345 $"[{now}] evts:{events.Length} msgs:{messages.Count} errs:{errors.Count}"
343 $"[{now}] evts:{events.Length} msgs:{messages.Count} errs:{errors.Count}"
346 + (if errors.Count > 0 then $" [{errors.[0]}]" else "")
344 + (if errors.Count > 0 then $" [{errors.[0]}]" else "")
347
345
348− return PollResult(Seq.toList messages, status, newCursor, events |> List.map (fun (_, _, x) -> x))
346+ return PollResult(Seq.toList messages, status, newCursor)
349 with
347 with
350− | :? System.Threading.Tasks.TaskCanceledException ->
348+ | :? TimeoutException ->
351− // Normal timeout from long poll — just retry
352− return PollResult([], "polling...", cursor, [])
349+ return PollResult([], "polling...", cursor)
353 | ex ->
350 | ex ->
354 let now = DateTimeOffset.UtcNow.ToString("HH:mm:ss")
351 let now = DateTimeOffset.UtcNow.ToString("HH:mm:ss")
355 do! Async.Sleep 3000
352 do! Async.Sleep 3000
356− return PollResult([], $"[{now}] poll error: {ex.Message}", cursor, [])
353+ return PollResult([], $"[{now}] poll error: {ex.Message}", cursor)
357 })
354 })
358
355
359 | CmdCopyToClipboard text ->
356 | CmdCopyToClipboard text ->
@@ -451,10 +448,6 @@ module App =
451 Label($"poll: {model.PollStatus}")
448 Label($"poll: {model.PollStatus}")
452 .font(size = 10.)
449 .font(size = 10.)
453 .textColor(Colors.DimGray)
450 .textColor(Colors.DimGray)
454−
455− Label($"Events: {model.Events}")
456− .font(10)
457− .textColor(Colors.DimGray)
458
451
459 if contactNames <> "" then
452 if contactNames <> "" then
460 Label($"names: {contactNames}")
453 Label($"names: {contactNames}")
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/_CodeSignature/CodeResources+4−4
@@ -162,11 +162,11 @@
162 </data>
162 </data>
163 <key>Skrepka.dll</key>
163 <key>Skrepka.dll</key>
164 <data>
164 <data>
165− di0VTroRrk0Osy30exH9yUkakk8=
165+ r/iZ42W7ZFINtqwUABz6aXZb5Pk=
166 </data>
166 </data>
167 <key>Skrepka.pdb</key>
167 <key>Skrepka.pdb</key>
168 <data>
168 <data>
169− /Vq3uwq4IB+ENxjl99BAiVqJSyI=
169+ zasvE0a+Wo6/nzeFiWsUuXJ8xvk=
170 </data>
170 </data>
171 <key>Sodium.Core.dll</key>
171 <key>Sodium.Core.dll</key>
172 <data>
172 <data>
@@ -1382,14 +1382,14 @@
1382 <dict>
1382 <dict>
1383 <key>hash2</key>
1383 <key>hash2</key>
1384 <data>
1384 <data>
1385− TzHV+4wEf6Ct4OfAEhd4ZU3QwzVFyBaCT50Uet/ckAA=
1385+ 0RquViMJ5bGDhRzrCP7NG5r1xRpnBdss3r87Qcq7Kms=
1386 </data>
1386 </data>
1387 </dict>
1387 </dict>
1388 <key>Skrepka.pdb</key>
1388 <key>Skrepka.pdb</key>
1389 <dict>
1389 <dict>
1390 <key>hash2</key>
1390 <key>hash2</key>
1391 <data>
1391 <data>
1392− AR+okdAV5ZOA0Rj5RzoaFK5GZ4bSMLlz0J92sjaMxdY=
1392+ cUBl9b6I54xAic0oMZhTa+FIkXNVu6LHRMxd5Q4QIVE=
1393 </data>
1393 </data>
1394 </dict>
1394 </dict>
1395 <key>Sodium.Core.dll</key>
1395 <key>Sodium.Core.dll</key>
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/_CodeSignature/CodeResources+2−2
@@ -240,14 +240,14 @@
240 <dict>
240 <dict>
241 <key>hash2</key>
241 <key>hash2</key>
242 <data>
242 <data>
243− fLiyHbdfTDvK9D25CmTS2vrZbxslf5o37h7stAFyyzc=
243+ 1YMNcB7xDPXVmXTy/5unpWgH6sFYXx+ajmUweDpLEA0=
244 </data>
244 </data>
245 </dict>
245 </dict>
246 <key>MonoBundle/Skrepka.pdb</key>
246 <key>MonoBundle/Skrepka.pdb</key>
247 <dict>
247 <dict>
248 <key>hash2</key>
248 <key>hash2</key>
249 <data>
249 <data>
250− UYV1Js3NxCMbEhwDushUOfVNf2Zqfoff+tT+ijnSNAw=
250+ W+NR34Zhwrd+2gZJtPCFpFd/OzgeckZ7aIj40k0IFrw=
251 </data>
251 </data>
252 </dict>
252 </dict>
253 <key>MonoBundle/Sodium.Core.dll</key>
253 <key>MonoBundle/Sodium.Core.dll</key>
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+b018bb70ad38e33f052804139428cc5ad37cf583")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+ac8c87f242c436f4a1bad89ff035eecf82a2bb15")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−57e7ff8ad3251b11511c75e432d109c5c81fd656f2685113d1e9afa4e3a11850
1+dcd7cb11d85d1b7e19d268b4a83cd5e3289a020ad2c4daa5bfa952d60173855b
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/b018bb70ad38e33f052804139428cc5ad37cf583/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/ac8c87f242c436f4a1bad89ff035eecf82a2bb15/*"}}
client/obj/Debug/net10.0-ios/iossimulator-arm64/linked/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/linked/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/refint/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+b018bb70ad38e33f052804139428cc5ad37cf583")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+ac8c87f242c436f4a1bad89ff035eecf82a2bb15")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−1884cb8a1baa514aa08abae9e9488933dc9303d202768ad3435ac63803776e48
1+fb7049c87bbc40133ce9fcdb916d1edeb0d2c72163aa8e3476623d121e5e3788
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/b018bb70ad38e33f052804139428cc5ad37cf583/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/ac8c87f242c436f4a1bad89ff035eecf82a2bb15/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/linked/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/linked/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.

server.knot+22−22
@@ -40,9 +40,9 @@ maxForwardRetriesPerServer = 100
40-- Types
40-- Types
41-- ============================================
41-- ============================================
42
42
43−type PollEvent = {id: Text, eventType: Text, payload: {encryptedBlob: Text, fromKey: Text, timestamp: Int, toKey: Text}}
43+type PollEvent = {id: Text, eventType: Text, payload: {encryptedBlob: Text, fromKey: Text, timestamp: Timestamp, toKey: Text}}
44
44
45−type GossipEvent = {eventType: Text, pubkey: Text, server: Text, timestamp: Int, ttl: Int}
45+type GossipEvent = {eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}
46
46
47type PubkeyHex = Text where \s -> isValidHex s && not (tooLong s maxPubkeyLen)
47type PubkeyHex = Text where \s -> isValidHex s && not (tooLong s maxPubkeyLen)
48type BlobHex = Text where \s -> isValidHex s && not (tooLong s maxBlobLen)
48type BlobHex = Text where \s -> isValidHex s && not (tooLong s maxBlobLen)
@@ -50,6 +50,8 @@ type SignatureHex = Text where \s -> isValidHex s && not (tooLong s maxSignature
50type MessageIdHex = Text where \s -> isValidHex s && not (tooLong s maxMessageIdLen)
50type MessageIdHex = Text where \s -> isValidHex s && not (tooLong s maxMessageIdLen)
51type ServerName = Text where \s -> not (tooLong s maxServerNameLen) && not (isBadServerName s)
51type ServerName = Text where \s -> not (tooLong s maxServerNameLen) && not (isBadServerName s)
52
52
53+type Timestamp = Int<Ms>
54+
53-- ============================================
55-- ============================================
54-- Persisted State
56-- Persisted State
55-- ============================================
57-- ============================================
@@ -58,13 +60,13 @@ type ServerName = Text where \s -> not (tooLong s maxServerNameLen) && not (isBa
58
60
59*sessions : [{token: Text, pubkey: Text, expiresAt: Int}]
61*sessions : [{token: Text, pubkey: Text, expiresAt: Int}]
60
62
61−*messages : [{messageId: Text, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Int, originServer: Text, receivedAt: Int}]
63+*messages : [{messageId: Text, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Timestamp, originServer: Text, receivedAt: Int}]
62
64
63−*presence : [{pubkey: Text, server: Text, expiresAt: Int, timestamp: Int}]
65+*presence : [{pubkey: Text, server: Text, expiresAt: Int, timestamp: Timestamp}]
64
66
65*seqCounter : Int
67*seqCounter : Int
66
68
67−*forwards : [{messageId: Text, toServer: Text, createdAt: Int, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Int}]
69+*forwards : [{messageId: Text, toServer: Text, createdAt: Int, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Timestamp}]
68
70
69*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int, originServer: Text}]
71*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int, originServer: Text}]
70
72
@@ -88,7 +90,7 @@ route ClientApi where
88 -> {events: [PollEvent], cursor: Int, authorized: Bool}
90 -> {events: [PollEvent], cursor: Int, authorized: Bool}
89 = Poll
91 = Poll
90 /messages
92 /messages
91− POST {to: PubkeyHex, encryptedBlob: BlobHex, timestamp: Int} / headers {authorization: Text}
93+ POST {to: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp} / headers {authorization: Text}
92 -> {status: Text, messageId: Text}
94 -> {status: Text, messageId: Text}
93 = SendMessage
95 = SendMessage
94 POST {messageIds: [MessageIdHex]} /ack headers {authorization: Text}
96 POST {messageIds: [MessageIdHex]} /ack headers {authorization: Text}
@@ -103,7 +105,7 @@ route FederationApi where
103 POST {events: [GossipEvent], hopsRemaining: Int, fromServer: ServerName} /gossip headers {authorization: Text}
105 POST {events: [GossipEvent], hopsRemaining: Int, fromServer: ServerName} /gossip headers {authorization: Text}
104 -> {ok: Bool}
106 -> {ok: Bool}
105 = RecvGossip
107 = RecvGossip
106− POST {toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Int, originServer: ServerName, messageId: MessageIdHex} /forward headers {authorization: Text}
108+ POST {toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp, originServer: ServerName, messageId: MessageIdHex} /forward headers {authorization: Text}
107 -> {ok: Bool}
109 -> {ok: Bool}
108 = ForwardMessage
110 = ForwardMessage
109 POST {messageIds: [Text], originServer: Text, fromServer: ServerName} /ack headers {authorization: Text}
111 POST {messageIds: [Text], originServer: Text, fromServer: ServerName} /ack headers {authorization: Text}
@@ -121,6 +123,9 @@ route Api = AuthApi | ClientApi | FederationApi | MaintenanceApi
121-- Helpers
123-- Helpers
122-- ============================================
124-- ============================================
123
125
126+msToS : Int<Ms> -> Int<S>
127+msToS = \x -> x / 1000
128+
124hexDigit = \n ->
129hexDigit = \n ->
125 let n = if n < 0 then 0 else if n > 15 then 15 else n in
130 let n = if n < 0 then 0 else if n > 15 then 15 else n in
126 if n == 0 then "0" else if n == 1 then "1" else if n == 2 then "2" else if n == 3 then "3"
131 if n == 0 then "0" else if n == 1 then "1" else if n == 2 then "2" else if n == 3 then "3"
@@ -414,14 +419,12 @@ sendToEach = \servers action ->
414 sendToEach rest action
419 sendToEach rest action
415
420
416pruneExpired = \t -> do
421pruneExpired = \t -> do
417− challenges <- *challenges
418 set *challenges = do
422 set *challenges = do
419− c <- challenges
423+ c <- *challenges
420 where c.expiresAt > t
424 where c.expiresAt > t
421 yield c
425 yield c
422− sessions <- *sessions
423 set *sessions = do
426 set *sessions = do
424− s <- sessions
427+ s <- *sessions
425 where s.expiresAt > t
428 where s.expiresAt > t
426 yield s
429 yield s
427 presenceBefore <- *presence
430 presenceBefore <- *presence
@@ -434,19 +437,16 @@ pruneExpired = \t -> do
434 where p.expiresAt > t
437 where p.expiresAt > t
435 yield p
438 yield p
436 set *presence = presenceAfter
439 set *presence = presenceAfter
437− messages <- *messages
438 set *messages = do
440 set *messages = do
439− m <- messages
441+ m <- *messages
440 where m.receivedAt >= t - messageMaxAge
442 where m.receivedAt >= t - messageMaxAge
441 yield m
443 yield m
442− forwards <- *forwards
443 set *forwards = do
444 set *forwards = do
444− f <- forwards
445+ f <- *forwards
445 where f.createdAt >= t - messageMaxAge
446 where f.createdAt >= t - messageMaxAge
446 yield f
447 yield f
447− ackedIds <- *ackedIds
448 set *ackedIds = do
448 set *ackedIds = do
449− a <- ackedIds
449+ a <- *ackedIds
450 where a.ackedAt >= t - messageMaxAge
450 where a.ackedAt >= t - messageMaxAge
451 yield a
451 yield a
452 yield {expiredLocal: (do
452 yield {expiredLocal: (do
@@ -468,7 +468,7 @@ maybePrune = \t -> do
468 set *lastPrunedAt = t
468 set *lastPrunedAt = t
469 pruneExpired t
469 pruneExpired t
470
470
471−sendBatchedGossipToServer : Text -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Int, ttl: Int}] -> IO {network} {}
471+sendBatchedGossipToServer : Text -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int}] -> IO {network} {}
472sendBatchedGossipToServer = \server allEvents ->
472sendBatchedGossipToServer = \server allEvents ->
473 if count allEvents == 0
473 if count allEvents == 0
474 then yield {}
474 then yield {}
@@ -485,7 +485,7 @@ sendBatchedGossipToServer = \server allEvents ->
485 Err _ -> yield {}
485 Err _ -> yield {}
486 sendBatchedGossipToServer server rest
486 sendBatchedGossipToServer server rest
487
487
488−sendBatchedGossip : [Text] -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Int, ttl: Int}] -> IO {network} {}
488+sendBatchedGossip : [Text] -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int}] -> IO {network} {}
489sendBatchedGossip = \peerServers allEvents ->
489sendBatchedGossip = \peerServers allEvents ->
490 if count allEvents == 0
490 if count allEvents == 0
491 then yield {}
491 then yield {}
@@ -506,7 +506,7 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
506 sendBatchedGossip peerServers gossipEvents
506 sendBatchedGossip peerServers gossipEvents
507 else yield {}
507 else yield {}
508
508
509−retryForwardsForServer : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Int, toKey: Text, toServer: Text}] -> Int -> IO {network} {}
509+retryForwardsForServer : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Timestamp, toKey: Text, toServer: Text}] -> Int -> IO {network} {}
510retryForwardsForServer = \entries remaining ->
510retryForwardsForServer = \entries remaining ->
511 if count entries == 0 || remaining <= 0
511 if count entries == 0 || remaining <= 0
512 then yield {}
512 then yield {}
@@ -532,7 +532,7 @@ retryForwardsForServer = \entries remaining ->
532 Err _ -> yield {}
532 Err _ -> yield {}
533 retryForwardsForServer rest (remaining - 1)
533 retryForwardsForServer rest (remaining - 1)
534
534
535−retryPendingForwards : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Int, toKey: Text, toServer: Text}] -> IO {network} {}
535+retryPendingForwards : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Timestamp, toKey: Text, toServer: Text}] -> IO {network} {}
536retryPendingForwards = \pending -> do
536retryPendingForwards = \pending -> do
537 let servers = fold (\acc x ->
537 let servers = fold (\acc x ->
538 if count (do s <- acc; where s == x.toServer; yield s) > 0 then acc
538 if count (do s <- acc; where s == x.toServer; yield s) > 0 then acc
@@ -636,7 +636,7 @@ pollLoop = \token cursor deadline -> do
636 if result.authorized && count result.events == 0 && t < deadline
636 if result.authorized && count result.events == 0 && t < deadline
637 then do
637 then do
638 let remaining = deadline - t
638 let remaining = deadline - t
639− let sleepMs = if remaining > 500 then 500 else remaining
639+ let sleepMs = if remaining > 500 then 500<Ms> else remaining
640 sleep sleepMs
640 sleep sleepMs
641 pollLoop token cursor deadline
641 pollLoop token cursor deadline
642 else yield result
642 else yield result