@@ -307,25 +307,14 @@ route Api = AuthApi | ClientApi | FederationApi
307307-- ============================================
308308-- Generic helpers
309309-- ============================================
310−-- List/predicate combinators (filter/any/none/findFirst/...), token generation,
310+-- List/predicate combinators (filter/all/findFirst/...), token generation,
311311-- and small text/server-name utilities used throughout the rest of the file.
312312-- Everything is intentionally O(n) over flat lists; performance is sacrificed
313313-- for clarity per the file header.
314314
315−-- Filter then map in one pass.
316−mapWhere : a b -> (b -> Bool) -> (b -> c) -> a c
317−mapWhere = \xs pred fn -> map fn (filter pred xs)
318−countWhere : a b -> (b -> Bool) -> Int
319−countWhere = \xs pred -> count (filter pred xs)
320−-- True when at least one element matches.
321−any : [a] -> (a -> Bool) -> Bool
322−any = \xs pred -> countWhere xs pred > 0
323−-- True when no element matches.
324−none : [a] -> (a -> Bool) -> Bool
325−none = \xs pred -> not any xs pred
326315-- True when every element matches.
327316all : [a] -> (a -> Bool) -> Bool
328−all = \xs pred -> none xs (\x -> not pred x)
317+all = \xs pred -> not (any (\x -> not pred x) xs)
329318findFirst : [a] -> (a -> Bool) -> Maybe a
330319findFirst = \xs pred -> headOf (filter pred xs)
331320-- Run `action` only when `cond` is True; otherwise no-op.
@@ -341,7 +330,7 @@ clamp = \lo hi x -> max lo (min hi x)
341330
342331-- First element of a list, or Nothing for an empty list.
343332headOf : [a] -> Maybe a
344−headOf = \items -> fold (\_ x -> Just {value: x}) (Nothing {}) (takeRelation 1 items)
333+headOf = \items -> fold (\_ x -> Just {value: x}) (Nothing {}) (take 1 items)
345334
346335-- Cap to maxN by dropping the smallest-key entries (LRU-style). Always sorts.
347336-- For session/challenge/rate caps, `getKey` is typically `expiresAt` or
@@ -359,7 +348,7 @@ capPerKey = \items pred maxN getKey -> union (filter (\x -> not pred x) items) (
359348-- Sequence an IO action over each item, in order. Recurses through the list
360349-- one element at a time; this is the closest equivalent of `mapM_` here.
361350forEach : [a] -> (a -> IO {} {}) -> IO {} {}
362−forEach = \items action -> case single (takeRelation 1 items) of
351+forEach = \items action -> case single (take 1 items) of
363352 Nothing {} -> yield {}
364353 Just {value: x} -> do
365354 action x
@@ -430,7 +419,7 @@ containsSuffix = \suffix s -> drop (max 0 (count (chars s) - count (chars suffix
430419-- (so we never `fetch` a peer pubkey from loopback) and on inbound requests
431420-- (so a peer can't claim a bogus `fromServer`).
432421isBadServerName : Text -> Bool
433−isBadServerName = \sn -> sn == "" || sn == "localhost" || not all (chars sn) (\c -> contains c "0123456789abcdefghijklmnopqrstuvwxyz-.:") || any [
422+isBadServerName = \sn -> sn == "" || sn == "localhost" || not all (chars sn) (\c -> contains c "0123456789abcdefghijklmnopqrstuvwxyz-.:") || any (\p -> hasPrefix p sn) [
434423 ".",
435424 ":",
436425 "[",
@@ -440,7 +429,7 @@ isBadServerName = \sn -> sn == "" || sn == "localhost" || not all (chars sn) (\c
440429 "169.254.",
441430 "0.",
442431 "::"
443−] (\p -> hasPrefix p sn) || any [".localhost", ".local", ".internal", ".arpa", ".onion"] (\s -> containsSuffix s sn)
432+] || any (\s -> containsSuffix s sn) [".localhost", ".local", ".internal", ".arpa", ".onion"]
444433
445434-- ============================================
446435-- Auth helpers
@@ -581,7 +570,7 @@ retryBackoff = \failures -> min retryBackoffMax (retryBackoffBase * max 1 failur
581570-- True when enough time has passed since the last failure for `server` to be
582571-- worth retrying (or no failure recorded at all).
583572shouldRetryServer : Text -> Timestamp -> [ForwardFailure] -> Bool
584−shouldRetryServer = \server t failures -> none failures (\f -> f.server == server && t - f.failedAt < retryBackoff f.failures)
573+shouldRetryServer = \server t failures -> not (any (\f -> f.server == server && t - f.failedAt < retryBackoff f.failures) failures)
585574
586575-- Bump the failure counter for `server`. Preserves `firstFailedAt` across
587576-- subsequent failures.
@@ -654,7 +643,7 @@ pruneExpired = \t -> atomic do
654643 -- presence: remember locally-expired keys for offline gossip
655644 pres <- *presence
656645 *presence = filter (\p -> p.expiresAt > t) pres
657− let stillLive = \pk -> any pres (\k -> isLocalServer k.server && k.pubkey == pk && k.expiresAt > t)
646+ let stillLive = \pk -> any (\k -> isLocalServer k.server && k.pubkey == pk && k.expiresAt > t) pres
658647 yield (filter (\p -> p.expiresAt <= t && isLocalServer p.server && not (stillLive p.pubkey)) pres)
659648
660649-- ============================================
@@ -702,7 +691,7 @@ retryPendingForwards = do
702691 t <- now
703692 fwds <- *forwards
704693 failures <- *forwardFailures
705− forEach (filter (\s -> shouldRetryServer s t failures) (collectPeers fwds (\f -> f.toServer))) (\server -> forEach (takeRelation maxBatchSize (filter (\f -> f.toServer == server) fwds)) (\f -> do
694+ forEach (filter (\s -> shouldRetryServer s t failures) (collectPeers fwds (\f -> f.toServer))) (\server -> forEach (take maxBatchSize (filter (\f -> f.toServer == server) fwds)) (\f -> do
706695 result <- fetch (federationUrl server) (ForwardMessage {f.toKey, f.encryptedBlob})
707696 success <- case result of
708697 Err _ -> do
@@ -724,13 +713,13 @@ forwardQueuedToServer : PubkeyHex -> ServerName -> IO {rw *forwards, r *messages
724713forwardQueuedToServer = \pubkey server -> atomic (do
725714 msgs <- *messages
726715 fwds <- *forwards
727− let candidates = filter (\m -> m.toKey == pubkey && none fwds (\f -> f.encryptedBlob == m.encryptedBlob && f.toServer == server)) msgs
716+ let candidates = filter (\m -> m.toKey == pubkey && not (any (\f -> f.encryptedBlob == m.encryptedBlob && f.toServer == server) fwds)) msgs
728717 let newFwds = map (\m -> {
729718 toServer: server,
730719 m.toKey,
731720 m.encryptedBlob,
732721 retries: 0
733− }) (takeRelation maxForwardsPerServer candidates)
722+ }) (take maxForwardsPerServer candidates)
734723 *forwards = union fwds newFwds)
735724
736725-- ============================================
@@ -811,7 +800,7 @@ handleVerifyAuth = \pubkey challenge signature revokeOthers clientIp -> do
811800 Just {value: sigBytes} -> verify pk (textToBytes challenge) sigBytes
812801 _ -> False {}
813802 _ -> False {}
814− let valid = any ch (\c -> same c && c.expiresAt > t && matchIp clientIp c.ip) && sigValid
803+ let valid = any (\c -> same c && c.expiresAt > t && matchIp clientIp c.ip) ch && sigValid
815804 if not valid
816805 then yield {valid: False {}, peers: []}
817806 else do
@@ -864,7 +853,7 @@ pollOnce = \authorization cursor clientIp t -> do
864853 startWakeup <- *pollWakeup
865854 pres <- *presence
866855 let mine = \p -> p.pubkey == pubkey && isLocalServer p.server
867− let stale = not any pres (\p -> mine p && p.expiresAt - t >= sessionExpiry / 2)
856+ let stale = not (any (\p -> mine p && p.expiresAt - t >= sessionExpiry / 2) pres)
868857 refreshPeers <- if stale
869858 then do
870859 let kept = filter (\p -> not mine p) pres
@@ -909,7 +898,7 @@ doPoll = \s t -> do
909898-- which the client echoes on the next /poll to advance — and which the
910899-- server treats as an implicit ack.
911900collectPollEvents : [Message] -> PubkeyHex -> Timestamp -> Timestamp -> {events: [PollEvent], cursor: Timestamp}
912−collectPollEvents = \messages pubkey cursor t -> (\limited -> {events: map (\m -> {m.encryptedBlob}) limited, cursor: fold (\acc m -> max acc m.receivedAt) cursor limited}) (takeRelation maxPollEvents (sortBy (\m -> m.receivedAt) (filter (\m -> m.toKey == pubkey && t - m.receivedAt < messageMaxAge && m.receivedAt > cursor) messages)))
901+collectPollEvents = \messages pubkey cursor t -> (\limited -> {events: map (\m -> {m.encryptedBlob}) limited, cursor: fold (\acc m -> max acc m.receivedAt) cursor limited}) (take maxPollEvents (sortBy (\m -> m.receivedAt) (filter (\m -> m.toKey == pubkey && t - m.receivedAt < messageMaxAge && m.receivedAt > cursor) messages)))
913902
914903-- Treat the supplied cursor as an ack: drop all messages with
915904-- receivedAt <= cursor, plus their pending forwards.
@@ -946,7 +935,7 @@ handleSendMessages : [{encryptedBlob: BlobHex, to: PubkeyHex | a}] -> Text -> Te
946935handleSendMessages = \messages authorization clientIp -> do
947936 t <- now
948937 gateAuth authorization clientIp t (\senderKey -> do
949− let limited = takeRelation maxBatchSize messages
938+ let limited = take maxBatchSize messages
950939 decision <- atomic (do
951940 checked <- mapEach limited (\m -> do
952941 c <- checkSendRate senderKey m.to t
@@ -999,12 +988,12 @@ commitSendMessage = \to encryptedBlob remoteServers t -> do
999988 *pollWakeup = seqTs
1000989 when federationEnabled (do
1001990 fwds <- *forwards
1002− let newFwds = mapWhere remoteServers (\s -> none fwds (\f -> f.encryptedBlob == encryptedBlob && f.toServer == s)) (\s -> {
991+ let newFwds = map (\s -> {
1003992 toServer: s,
1004993 toKey: to,
1005994 encryptedBlob,
1006995 retries: 0
1007− })
996+ }) (filter (\s -> not (any (\f -> f.encryptedBlob == encryptedBlob && f.toServer == s) fwds)) remoteServers)
1008997 *forwards = union fwds newFwds)
1009998
1010999-- Fire one /federation/forward call per pre-staged server. On success the
@@ -1031,8 +1020,8 @@ forkForwardsTo = \to encryptedBlob servers -> forEach servers (\server -> do
10311020 let remaining = filter (\x -> not same x) fwds2
10321021 *forwards = remaining
10331022 pres <- *presence
1034− let stillLocal = any pres (\p -> p.pubkey == to && isLocalServer p.server && p.expiresAt > t)
1035− let othersPending = any remaining (\x -> x.encryptedBlob == encryptedBlob)
1023+ let stillLocal = any (\p -> p.pubkey == to && isLocalServer p.server && p.expiresAt > t) pres
1024+ let othersPending = any (\x -> x.encryptedBlob == encryptedBlob) remaining
10361025 when (not stillLocal && not othersPending) (do
10371026 msgs <- *messages
10381027 *messages = filter (\m -> m.encryptedBlob != encryptedBlob) msgs)))
@@ -1073,7 +1062,7 @@ handleRecvGossip = \events fromServer -> do
10731062 let kept = filter (\p -> p.server != fromServer || not elem p.pubkey touchedPubkeys) pres
10741063 let added = map (\pk -> {pubkey: pk, server: fromServer, expiresAt: t + onlineGossipTtl}) onlinePubkeys
10751064 *presence = union kept added
1076− yield (filter (\pk -> none pres (\p -> p.pubkey == pk && p.server == fromServer && p.expiresAt > t)) onlinePubkeys))
1065+ yield (filter (\pk -> not (any (\p -> p.pubkey == pk && p.server == fromServer && p.expiresAt > t) pres)) onlinePubkeys))
10771066 fork (forEach newlyOnline (\pk -> forwardQueuedToServer pk fromServer))
10781067 yield (Ok {value: {}})
10791068
@@ -1089,7 +1078,7 @@ storeFederated : PubkeyHex -> BlobHex -> Int<Ms> -> IO {rw *messages, rw *pollWa
10891078storeFederated = \toKey encryptedBlob t -> do
10901079 pres <- *presence
10911080 msgs <- *messages
1092− if not any pres (\p -> p.pubkey == toKey && isLocalServer p.server && p.expiresAt > t)
1081+ if not (any (\p -> p.pubkey == toKey && isLocalServer p.server && p.expiresAt > t) pres)
10931082 then yield "no_presence"
10941083 else do
10951084 seq <- *seqCounter