on master help sign in
Switched from Al to Run
commit79d2af497ec8150daf313eadbce7b82b6aa759e0 authoriko <ilyakooo0@gmail.com> dateFri 5 Aug 2022, 17:03 (4 years ago) parent9331dcd
7 files changed+212−168
backend/BW/Logic.purs+84−32 backend/Main.purs+127−70 el/spago.dhall−14 el/src/Control/El.purs−23 el/test/Main.purs−27 packages.dhall−1 spago.dhall+1−1
backend/BW/Logic.purs+84−32
@@ -2,7 +2,6 @@ module BW.Logic where
22
33import BW
44import BW.Types
5−import Control.El
65import Prelude
76import Bridge as Bridge
87import Control.Monad.Error.Class (throwError)
@@ -13,38 +12,57 @@ import Data.Function.Uncurried (runFn2, runFn3, runFn4)
1312import Data.JNullable (JNullable, fromJNullable, jnull, nullify)
1413import Data.JNullable as JNullable
1514import Data.JOpt (fromJOpt)
16−import Data.Maybe (Maybe(..))
15+import Data.Maybe (Maybe(..), fromMaybe)
1716import Data.Newtype (class Newtype, wrap)
18−import Data.JNullable (jnull, nullify)
19−import Data.Maybe (Maybe, fromMaybe)
2017import Data.String as String
2118import Data.SymmetricCryptoKey (SymmetricCryptoKey)
2219import Data.SymmetricCryptoKey as SymmetricCryptoKey
2320import Data.Traversable (traverse)
21+import Effect (Effect)
22+import Effect.Aff (Aff)
2423import Effect.Aff.Class (class MonadAff, liftAff)
2524import Effect.Class (liftEffect)
2625import Effect.Exception (error)
2726import Effect.Exception as Exc
27+import Run (Run)
28+import Run.Reader (Reader, askAt)
29+import Type.Prelude (Proxy(..))
2830import Untagged.Union (type (|+|))
2931
3032-- | Creates the master key
3133makePreloginKey ::
3234 forall r.
33− HasL "crypto" CryptoService r =>
34− PreloginResponse -> Email -> Password -> Al r SymmetricCryptoKey
35+ PreloginResponse ->
36+ Email ->
37+ Password ->
38+ Run
39+ ( crypto ::
40+ Reader CryptoService
41+ , effect :: Effect
42+ , aff :: Aff
43+ | r
44+ )
45+ SymmetricCryptoKey
3546makePreloginKey { kdf, kdfIterations } (Email email') password = do
36− crypto <- l (L :: L "crypto")
47+ crypto <- askAt (Proxy :: _ "crypto")
3748 let
3849 email = (String.trim >>> String.toLower) email'
3950 liftPromise $ runFn4 crypto.makeKey password email kdf kdfIterations
4051
4152makeDecryptionKey ::
4253 forall r.
43− HasL "crypto" CryptoService r =>
4454 -- | Matser key (prelogin key)
45− SymmetricCryptoKey -> EncryptedString -> Al r SymmetricCryptoKey
55+ SymmetricCryptoKey ->
56+ EncryptedString ->
57+ Run
58+ ( crypto :: Reader CryptoService
59+ , effect :: Effect
60+ , aff :: Aff
61+ | r
62+ )
63+ SymmetricCryptoKey
4664makeDecryptionKey masterKey str = do
47− crypto <- l (L :: L "crypto")
65+ crypto <- askAt (Proxy :: _ "crypto")
4866 let
4967 encryptedKeyString = EncString.fromString str
5068
@@ -61,13 +79,22 @@ makeDecryptionKey masterKey str = do
6179
6280getLogInRequestToken ::
6381 forall r.
64− HasL "api" ApiService r =>
65− HasL "crypto" CryptoService r =>
66− PreloginResponse -> Email -> Password -> Maybe String -> Al r (IdentityCaptchaResponse |+| IdentityTokenResponse)
82+ PreloginResponse ->
83+ Email ->
84+ Password ->
85+ Maybe String ->
86+ Run
87+ ( api :: Reader ApiService
88+ , crypto :: Reader CryptoService
89+ , effect :: Effect
90+ , aff :: Aff
91+ | r
92+ )
93+ (IdentityCaptchaResponse |+| IdentityTokenResponse)
6794getLogInRequestToken prelogin email password captchaResponse = do
6895 key <- makePreloginKey prelogin email password
69− crypto <- l (L :: L "crypto")
70− api :: ApiService <- l (L :: L "api")
96+ crypto <- askAt (Proxy :: _ "crypto")
97+ api :: ApiService <- askAt (Proxy :: _ "api")
7198 _localHashedPassword <-
7299 liftPromise
73100 $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
@@ -90,20 +117,34 @@ getLogInRequestToken prelogin email password captchaResponse = do
90117 }
91118 }
92119
93−hashPassword :: forall r. HasL "cryptoFunctions" CryptoFunctions r => Password -> Al r Hash
120+hashPassword ::
121+ forall r.
122+ Password ->
123+ Run
124+ ( cryptoFunctions :: Reader CryptoFunctions
125+ , aff :: Aff
126+ , effect :: Effect
127+ | r
128+ )
129+ Hash
94130hashPassword (Password password) = do
95− cryptoFunctions <- l (L :: L "cryptoFunctions")
131+ cryptoFunctions <- askAt (Proxy :: _ "cryptoFunctions")
96132 liftPromise $ runFn2 cryptoFunctions.hash password cryptoFunctionsTypeSha512
97133
98134decrypt ::
99135 forall r.
100− HasL "crypto" CryptoService r =>
101− HasL "key" SymmetricCryptoKey r =>
102136 EncryptedString ->
103− Al r String
137+ Run
138+ ( key :: Reader SymmetricCryptoKey
139+ , crypto :: Reader CryptoService
140+ , aff :: Aff
141+ , effect :: Effect
142+ | r
143+ )
144+ String
104145decrypt input = do
105− crypto <- l (L :: L "crypto")
106− key <- l (L :: L "key")
146+ crypto <- askAt (Proxy :: _ "crypto")
147+ key <- askAt (Proxy :: _ "key")
107148 liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
108149
109150liftPromise ∷ forall m a. MonadAff m ⇒ Promise a → m a
@@ -111,9 +152,15 @@ liftPromise = liftAff <<< Promise.toAff
111152
112153decodeCipher ::
113154 forall r.
114− HasL "crypto" CryptoService r =>
115− HasL "key" SymmetricCryptoKey r =>
116− CipherResponse -> Al r Bridge.Sub_LoadCipher
155+ CipherResponse ->
156+ Run
157+ ( key :: Reader SymmetricCryptoKey
158+ , crypto :: Reader CryptoService
159+ , aff :: Aff
160+ , effect :: Effect
161+ | r
162+ )
163+ Bridge.Sub_LoadCipher
117164decodeCipher cipher = do
118165 name <- decrypt cipher.name
119166 cipherType <- case cipher.type of
@@ -124,7 +171,7 @@ decodeCipher cipher = do
124171 n
125172 | cipherTypeLogin == n -> do
126173 case JNullable.toMaybe cipher.login of
127− Nothing -> throwError $ error "Login data is missing"
174+ Nothing -> liftEffect $ throwError $ error "Login data is missing"
128175 Just login -> do
129176 username <- decryptNullable login.username
130177 password <- decryptNullable login.password
@@ -138,7 +185,7 @@ decodeCipher cipher = do
138185 n
139186 | cipherTypeCard == n -> do
140187 case JNullable.toMaybe cipher.card of
141− Nothing -> throwError $ error "Card data is missing"
188+ Nothing -> liftEffect $ throwError $ error "Card data is missing"
142189 Just card -> do
143190 cardholderName <- decryptNullable card.cardholderName
144191 number <- decryptNullable card.number
@@ -152,7 +199,7 @@ decodeCipher cipher = do
152199 n
153200 | cipherTypeIdentity == n -> do
154201 case JNullable.toMaybe cipher.identity of
155− Nothing -> throwError $ error "Identity data is missing"
202+ Nothing -> liftEffect $ throwError $ error "Identity data is missing"
156203 Just identity -> do
157204 firstName <- decryptNullable identity.firstName
158205 middleName <- decryptNullable identity.middleName
@@ -193,14 +240,19 @@ decodeCipher cipher = do
193240 , title
194241 , username
195242 }
196− n -> throwError $ error $ "Unsupported cipher type: " <> show n
243+ n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
197244 pure $ Bridge.Sub_LoadCipher { cipherType, name, id: cipher.id }
198245
199246decryptNullable ::
200247 forall x r.
201248 Newtype x (Maybe String) =>
202− HasL "crypto" CryptoService r =>
203− HasL "key" SymmetricCryptoKey r =>
204249 JNullable EncryptedString ->
205− Al r x
250+ Run
251+ ( key :: Reader SymmetricCryptoKey
252+ , crypto :: Reader CryptoService
253+ , aff :: Aff
254+ , effect :: Effect
255+ | r
256+ )
257+ x
206258decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
backend/Main.purs+127−70
@@ -3,15 +3,13 @@ module Main
33 ) where
44
55import Prelude
6−import BW (ApiService, CryptoService, Services)
6+import BW (ApiService, CryptoService, Services, CryptoFunctions)
77import BW as WB
88import BW.Logic (decodeCipher, decrypt, hashPassword, liftPromise)
99import BW.Logic as Logic
1010import BW.Types (CipherResponse, Email(..), Password(..), Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
1111import Bridge as Bridge
12−import Control.El (class HasL, Al, L(..), l)
1312import Control.Monad.Error.Class (catchError, throwError)
14−import Control.Monad.Reader (runReaderT)
1513import Data.Argonaut (class DecodeJson)
1614import Data.Array as Array
1715import Data.Clipboard as Clipboard
@@ -37,7 +35,11 @@ import FFI (Elm)
3735import FFI as FFI
3836import Localstorage (class StorageKey)
3937import Localstorage as Storage
38+import Run (AFF, EFFECT, Run, liftAff, runBaseAff')
39+import Run.Reader (Reader, askAt, runReaderAt)
4040import Storage (MasterPasswordHashKey(..), PreloginResponseKey(..), SyncKey(..), TokenKey(..), UrlsKey(..))
41+import Type.Prelude (Proxy(..))
42+import Type.Row (type (+))
4143import Untagged.Union (toEither1)
4244import Web.HTML as Html
4345import Web.HTML.Window as Window
@@ -55,21 +57,50 @@ main = do
5557 Ref.write (Just captchaToken) hCaptchaTokenRef
5658 Elm.send app Bridge.CaptchaDone
5759 let
58− run act = do
60+ runAff act = runElmAff app $ run $ act
61+
62+ run ::
63+ forall a.
64+ Run
65+ ( app :: Reader Elm
66+ , aff :: Aff
67+ , effect :: Effect
68+ , crypto :: Reader CryptoService
69+ , storage :: Reader Storage
70+ , services :: Reader Services
71+ , cryptoFunctions :: Reader CryptoFunctions
72+ )
73+ a ->
74+ Run (EFFECT + AFF + ()) a
75+ run act =
76+ runReaderAt (Proxy :: _ "app") app
77+ $ runReaderAt (Proxy :: _ "crypto") services.crypto
78+ $ runReaderAt (Proxy :: _ "storage") storage
79+ $ runReaderAt (Proxy :: _ "services") services
80+ $ runReaderAt (Proxy :: _ "cryptoFunctions") services.cryptoFunctions
81+ $ act
82+
83+ runWithDecryptionKey ::
84+ Run
85+ ( app :: Reader Elm
86+ , aff :: Aff
87+ , effect :: Effect
88+ , key :: Reader SymmetricCryptoKey
89+ , crypto :: Reader CryptoService
90+ , storage :: Reader Storage
91+ , services :: Reader Services
92+ , cryptoFunctions :: Reader CryptoFunctions
93+ )
94+ Unit ->
95+ Effect Unit
96+ runWithDecryptionKey act = do
5997 maybeKey <- Ref.read masterKeyRef
60− runElmAff app $ flip runReaderT { app: app, crypto: services.crypto, storage: storage }
98+ runAff
6199 $ case maybeKey of
62100 Just masterKey -> do
63101 token <- getOrReset TokenKey
64102 key <- Logic.makeDecryptionKey masterKey token.key
65− liftEffect $ runElmAff app
66− $ runReaderT act
67− { app
68− , storage
69− , services
70− , crypto: services.crypto
71− , key
72− }
103+ runReaderAt (Proxy :: _ "key") key $ act
73104 Nothing -> requestMasterPassword
74105 Elm.subscribe app \cmd -> case cmd of
75106 Bridge.Login (Bridge.Cmd_Login { email, server, password }) -> do
@@ -77,22 +108,17 @@ main = do
77108 Ref.write Nothing hCaptchaTokenRef
78109 let
79110 urls = baseUrl server
80− runElmAff app do
111+ runAff do
81112 unauthedApi <- liftPromise $ services.getApi urls jnull
82− let
83− env =
84− { api: unauthedApi
85− , crypto: services.crypto
86− , storage
87− , cryptoFunctions: services.cryptoFunctions
88− , app
89− }
90− flip runReaderT env do
91− prelogin <- liftPromise $ unauthedApi.postPrelogin { email }
113+ prelogin <- liftPromise $ unauthedApi.postPrelogin { email }
114+ runReaderAt (Proxy :: _ "api") unauthedApi do
92115 loginResponse <-
93− catchError
94− (Logic.getLogInRequestToken prelogin (Email email) (Password password) captchaToken)
95− (const $ liftEffect $ Exc.throw "Could not log in. Please check your data and try again.")
116+ liftAff
117+ $ catchError
118+ ( runBaseAff' $ run $ runReaderAt (Proxy :: _ "api") unauthedApi
119+ $ Logic.getLogInRequestToken prelogin (Email email) (Password password) captchaToken
120+ )
121+ (const $ liftEffect $ Exc.throw "Could not log in. Please check your data and try again.")
96122 case toEither1 loginResponse of
97123 Left { siteKey } -> send $ Bridge.NeedsCaptcha siteKey
98124 Right token -> do
@@ -110,7 +136,7 @@ main = do
110136 Storage.store storage TokenKey token
111137 send Bridge.LoginSuccessful
112138 Bridge.NeedCiphersList ->
113− run do
139+ runWithDecryptionKey do
114140 sync <- getOrReset SyncKey
115141 ciphers <- traverse processCipher sync.ciphers
116142 let
@@ -121,21 +147,18 @@ main = do
121147 WebStorage.clear storage
122148 Elm.send app Bridge.Reset
123149 Bridge.SendMasterPassword masterPassword ->
124− runElmAff app
125− $ do
126− flip runReaderT { app: app, storage: storage, crypto: services.crypto, cryptoFunctions: services.cryptoFunctions }
127− $ do
128− hash <- getOrReset MasterPasswordHashKey
129− newHash <- hashPassword (Password masterPassword)
130− if hash == newHash then do
131− sync <- getOrReset SyncKey
132− prelogin <- getOrReset PreloginResponseKey
133− masterKey <- Logic.makePreloginKey prelogin (Email sync.profile.email) (Password masterPassword)
134− liftEffect $ Ref.write (Just masterKey) masterKeyRef
135− send Bridge.LoginSuccessful
136− else do
137− send $ Bridge.Error "The password is wrong. Please try again."
138− requestMasterPassword
150+ runAff do
151+ hash <- getOrReset MasterPasswordHashKey
152+ newHash <- hashPassword (Password masterPassword)
153+ if hash == newHash then do
154+ sync <- getOrReset SyncKey
155+ prelogin <- getOrReset PreloginResponseKey
156+ masterKey <- Logic.makePreloginKey prelogin (Email sync.profile.email) (Password masterPassword)
157+ liftEffect $ Ref.write (Just masterKey) masterKeyRef
158+ send Bridge.LoginSuccessful
159+ else do
160+ send $ Bridge.Error "The password is wrong. Please try again."
161+ requestMasterPassword
139162 Bridge.Init -> do
140163 liftEffect $ Storage.get storage TokenKey
141164 >>= \x ->
@@ -143,7 +166,7 @@ main = do
143166 Just _ -> Bridge.LoginSuccessful
144167 Nothing -> Bridge.NeedsLogin
145168 Bridge.RequestCipher id ->
146− run do
169+ runWithDecryptionKey do
147170 sync <- getOrReset SyncKey
148171 case Array.find (\c -> c.id == id) sync.ciphers of
149172 Nothing -> pure unit
@@ -153,20 +176,26 @@ main = do
153176 pure unit
154177 pure unit
155178 Bridge.NeedEmail ->
156− run do
179+ runWithDecryptionKey do
157180 sync <- getOrReset SyncKey
158181 send $ Bridge.RecieveEmail sync.profile.email
159182 pure unit
160183 Bridge.Copy text ->
161− run do
184+ runWithDecryptionKey do
162185 liftPromise $ Clipboard.clipboard text
163186 Bridge.Open uri -> openURL uri
164187
165188processCipher ::
166189 forall r.
167− HasL "crypto" CryptoService r =>
168− HasL "key" SymmetricCryptoKey r =>
169− CipherResponse -> Al r { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime }
190+ CipherResponse ->
191+ Run
192+ ( crypto :: Reader CryptoService
193+ , key :: Reader SymmetricCryptoKey
194+ , aff :: Aff
195+ , effect :: Effect
196+ | r
197+ )
198+ { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime }
170199processCipher cipher = do
171200 name <- decrypt cipher.name
172201 cipherType <- case cipher.type of
@@ -178,7 +207,7 @@ processCipher cipher = do
178207 | n == cipherTypeCard -> pure Bridge.CardType
179208 n
180209 | n == cipherTypeIdentity -> pure Bridge.IdentityType
181− n -> throwError $ error $ "Unsupported cipher type: " <> show n
210+ n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
182211 date <- liftEffect $ Timestamp.toDateTime cipher.revisionDate
183212 pure
184213 $ { cipher:
@@ -191,15 +220,17 @@ processCipher cipher = do
191220 , date
192221 }
193222
194−runElmAff :: FFI.Elm -> Aff Unit -> Effect Unit
223+runElmAff :: FFI.Elm -> Run (AFF + EFFECT + ()) Unit -> Effect Unit
195224runElmAff app =
196225 runAff_
197− $ \res -> do
226+ ( \res -> do
198227 case res of
199228 Left e -> do
200229 log $ show e
201230 Elm.send app (Bridge.Error $ Exc.message e)
202231 Right unit -> pure unit
232+ )
233+ <<< runBaseAff'
203234
204235baseUrl :: String -> Urls
205236baseUrl server =
@@ -215,46 +246,72 @@ baseUrl server =
215246
216247getOrReset ::
217248 forall k t r.
218− HasL "app" Elm r =>
219− HasL "storage" Storage r =>
220249 StorageKey k t =>
221250 DecodeJson t =>
222− k -> Al r t
251+ k ->
252+ Run
253+ ( storage :: Reader Storage
254+ , app :: Reader Elm
255+ , effect :: Effect
256+ | r
257+ )
258+ t
223259getOrReset key = do
224− storage <- l (L :: L "storage")
260+ storage <- askAt (Proxy :: _ "storage")
261+ app <- askAt (Proxy :: _ "app")
225262 let
226263 handleError = do
227− app <- l (L :: L "app")
228− liftEffect $ WebStorage.clear storage
229− liftEffect $ Elm.send app Bridge.Reset
230− liftEffect $ Exc.throw "Encountered an internal error. Resetting the app."
231− try (liftEffect $ Storage.get storage key)
264+ WebStorage.clear storage
265+ Elm.send app Bridge.Reset
266+ Exc.throw "Encountered an internal error. Resetting the app."
267+ liftEffect $ try (Storage.get storage key)
232268 >>= \x -> case x of
233269 Left err -> do
234− liftEffect $ log $ show err
270+ log $ show err
235271 handleError
236272 Right Nothing -> handleError
237273 Right (Just y) -> pure y
238274
239275getAuthedApi ::
240276 forall r.
241− HasL "services" Services r =>
242− HasL "storage" Storage r =>
243− HasL "app" Elm r =>
244− Al r ApiService
277+ Run
278+ ( storage :: Reader Storage
279+ , app :: Reader Elm
280+ , aff :: Aff
281+ , effect :: Effect
282+ , services :: Reader Services
283+ | r
284+ )
285+ ApiService
245286getAuthedApi = do
246− services <- l (L :: L "services")
287+ services <- askAt (Proxy :: _ "services")
247288 token <- getOrReset TokenKey
248289 url <- getOrReset UrlsKey
249290 liftPromise $ services.getApi (baseUrl url) (nullify token)
250291
251−requestMasterPassword :: forall r. HasL "storage" Storage r => HasL "app" Elm r => Al r Unit
292+requestMasterPassword ::
293+ forall r.
294+ Run
295+ ( storage :: Reader Storage
296+ , app :: Reader Elm
297+ , effect :: Effect
298+ | r
299+ )
300+ Unit
252301requestMasterPassword = do
253302 url <- getOrReset UrlsKey
254303 sync <- getOrReset SyncKey
255304 send $ Bridge.NeedsMasterPassword $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
256305
257−send :: forall r. HasL "app" Elm r => Bridge.Sub -> Al r Unit
306+send ::
307+ forall r.
308+ Bridge.Sub ->
309+ Run
310+ ( app :: Reader Elm
311+ , effect :: Effect
312+ | r
313+ )
314+ Unit
258315send sub = do
259− app <- l (L :: L "app")
316+ app <- askAt (Proxy :: _ "app")
260317 liftEffect $ Elm.send app sub
el/spago.dhalldeleted−14
@@ -1,14 +0,0 @@
1−{ name = "bw"
2−, dependencies =
3− [ "aff"
4− , "effect"
5− , "newtype"
6− , "prelude"
7− , "record"
8− , "test-unit"
9− , "transformers"
10− , "unsafe-coerce"
11− ]
12−, packages = ../packages.dhall
13−, sources = [ "src/**/*.purs", "test/**/*.purs" ]
14−}
el/src/Control/El.pursdeleted−23
@@ -1,23 +0,0 @@
1−module Control.El (El, Al, l, class HasL, L(..)) where
2−
3−
4−import Control.Monad.Reader (class MonadAsk, ReaderT, asks)
5−import Data.Symbol (class IsSymbol, reflectSymbol)
6−import Effect (Effect)
7−import Effect.Aff (Aff)
8−import Prim.Row (class Cons, class Nub)
9−import Record.Unsafe (unsafeGet)
10−import Type.Proxy (Proxy(..))
11−
12−data L :: forall k. k -> Type
13−data L t = L
14−
15−type El r = ReaderT (Record r) Effect
16−type Al r = ReaderT (Record r) Aff
17−
18−l :: forall res r field m. MonadAsk (Record r) m => HasL field res r => IsSymbol field => L field -> m res
19−l L = asks (unsafeGet (reflectSymbol (Proxy :: Proxy field)))
20−
21−class HasL :: forall k1 k2 k3. k1 -> k2 -> k3 -> Constraint
22−class HasL field t r | field r -> t
23−instance (Cons field t r r', Nub r' r) => HasL field t r
el/test/Main.pursdeleted−27
@@ -1,27 +0,0 @@
1−module Test.Main
2− ( main
3− ) where
4−
5−import Prelude
6−import Test.Unit
7−import Control.El
8−import Control.Monad.Reader (runReaderT)
9−import Effect (Effect)
10−import Effect.Aff (launchAff_)
11−import Test.Unit.Assert as Assert
12−import Test.Unit.Output.Fancy (runTest)
13−import Type.Proxy (Proxy(..))
14−
15−main ∷ Effect Unit
16−main =
17− launchAff_ $ void
18− $ runTest do
19− suite "sync code" do
20− test "arithmetic" do
21− x <- runReaderT singleHandler { handle: 2, ignored: "Hello" }
22− Assert.equal x 2
23−
24−singleHandler :: forall r. HasL "handle" Int r => Al r Int
25−singleHandler = do
26− x <- l (L :: L "handle")
27− pure $ x + 1
packages.dhall−1
@@ -103,7 +103,6 @@ let upstream =
103103 sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031
104104
105105in upstream
106− with el = ./el/spago.dhall as Location
107106 with argonaut-aeson-generic =
108107 { dependencies =
109108 [ "argonaut-codecs"
spago.dhall+1−1
@@ -26,7 +26,6 @@ to generate this file without the comments in this block.
2626 , "datetime-parsing"
2727 , "effect"
2828 , "either"
29− , "el"
3029 , "exceptions"
3130 , "foldable-traversable"
3231 , "functions"
@@ -36,6 +35,7 @@ to generate this file without the comments in this block.
3635 , "orders"
3736 , "prelude"
3837 , "refs"
38+ , "run"
3939 , "strings"
4040 , "transformers"
4141 , "tuples"