on master help sign in
Got all buttons working
e451fb6iko
appname.desktop+1−1
@@ -1,6 +1,6 @@
1[Desktop Entry]
1[Desktop Entry]
2Name=App Title
2Name=App Title
3−Exec=webapp-container --local-content-can-access-remote-urls --app-id="appname.iko" $@ index.html
3+Exec=webapp-container --webappUrlPatterns=index.html --local-content-can-access-remote-urls --app-id="appname.iko" $@ index.html
4Icon=logo.svg
4Icon=logo.svg
5Terminal=false
5Terminal=false
6Type=Application
6Type=Application
backend/Bridge.purs+3−1
@@ -28,11 +28,13 @@ derive instance eqCmd_Login :: Eq Cmd_Login
28derive instance ordCmd_Login :: Ord Cmd_Login
28derive instance ordCmd_Login :: Ord Cmd_Login
29
29
30data Cmd =
30data Cmd =
31+ Copy String
31− Init
32+ | Init
32 | Login Cmd_Login
33 | Login Cmd_Login
33 | NeedCiphersList
34 | NeedCiphersList
34 | NeedEmail
35 | NeedEmail
35 | NeedsReset
36 | NeedsReset
37+ | Open String
36 | RequestCipher String
38 | RequestCipher String
37 | SendMasterPassword String
39 | SendMasterPassword String
38
40
backend/Data/Clipboard.jsnew file+4
@@ -0,0 +1,4 @@
1+
2+export function clipboard(text) {
3+ return navigator.clipboard.writeText(text)
4+}
backend/Data/Clipboard.pursnew file+6
@@ -0,0 +1,6 @@
1+module Data.Clipboard where
2+
3+import Prelude
4+import Control.Promise (Promise)
5+
6+foreign import clipboard :: String -> Promise Unit
backend/Data/OpenURL.jsnew file+3
@@ -0,0 +1,3 @@
1+export function openURL(url) {
2+ return () => window.open(url, '_blank')
3+}
backend/Data/OpenURL.pursnew file+6
@@ -0,0 +1,6 @@
1+module Data.OpenURL where
2+
3+import Prelude
4+import Effect (Effect)
5+
6+foreign import openURL :: String -> Effect Unit
backend/Main.purs+5−3
@@ -9,19 +9,19 @@ import BW as WB
9import BW.Logic (decrypt, hashPassword, liftPromise)
9import BW.Logic (decrypt, hashPassword, liftPromise)
10import BW.Logic as Logic
10import BW.Logic as Logic
11import BW.Types (CipherResponse, Email(..), IdentityTokenResponse, Password(..), PreloginResponse, SyncResponse, Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
11import BW.Types (CipherResponse, Email(..), IdentityTokenResponse, Password(..), PreloginResponse, SyncResponse, Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
12−import Bridge (Sub_LoadCipher(..))
13import Bridge as Bridge
12import Bridge as Bridge
14import Control.El (class HasL, Al, L(..), l)
13import Control.El (class HasL, Al, L(..), l)
15import Control.Monad.Error.Class (throwError)
14import Control.Monad.Error.Class (throwError)
16−import Control.Monad.List.Trans as List
17import Control.Monad.Reader (runReaderT)
15import Control.Monad.Reader (runReaderT)
18import Data.Argonaut (class DecodeJson)
16import Data.Argonaut (class DecodeJson)
19import Data.Array as Array
17import Data.Array as Array
18+import Data.Clipboard as Clipboard
20import Data.Either (Either(..))
19import Data.Either (Either(..))
21import Data.JNullable (fromJNullable, jnull, nullify)
20import Data.JNullable (fromJNullable, jnull, nullify)
22import Data.JNullable as JNullable
21import Data.JNullable as JNullable
23import Data.JOpt (fromJOpt)
22import Data.JOpt (fromJOpt)
24import Data.Maybe (Maybe(..))
23import Data.Maybe (Maybe(..))
24+import Data.OpenURL (openURL)
25import Data.SymmetricCryptoKey (SymmetricCryptoKey)
25import Data.SymmetricCryptoKey (SymmetricCryptoKey)
26import Data.Timestamp as Timestamp
26import Data.Timestamp as Timestamp
27import Data.Traversable (traverse)
27import Data.Traversable (traverse)
@@ -95,7 +95,6 @@ main = do
95 ciphers <- traverse processCipher sync.ciphers
95 ciphers <- traverse processCipher sync.ciphers
96 liftEffect $ Elm.send app $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List ciphers
96 liftEffect $ Elm.send app $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List ciphers
97 api <- getAuthedApi
97 api <- getAuthedApi
98− sync <- liftPromise $ api.getSync unit
99 liftEffect $ Storage.store storage SyncKey sync
98 liftEffect $ Storage.store storage SyncKey sync
100 pure unit
99 pure unit
101 Bridge.NeedsReset -> do
100 Bridge.NeedsReset -> do
@@ -206,6 +205,9 @@ main = do
206 sync <- getOrReset storage SyncKey
205 sync <- getOrReset storage SyncKey
207 send $ Bridge.RecieveEmail sync.profile.email
206 send $ Bridge.RecieveEmail sync.profile.email
208 pure unit
207 pure unit
208+ Bridge.Copy text -> run do
209+ liftPromise $ Clipboard.clipboard text
210+ Bridge.Open uri -> openURL uri
209
211
210data SyncKey = SyncKey
212data SyncKey = SyncKey
211
213
bridge.yaml+2
@@ -90,3 +90,5 @@ Cmd:
90- NeedsReset
90- NeedsReset
91- RequestCipher: String
91- RequestCipher: String
92- NeedEmail
92- NeedEmail
93+- Copy: String
94+- Open: String
frontend/Bridge.elm+8−2
@@ -32,22 +32,26 @@ jsonEncCmd_Login val =
32
32
33
33
34type Cmd =
34type Cmd =
35+ Copy String
35− Init
36+ | Init
36 | Login Cmd_Login
37 | Login Cmd_Login
37 | NeedCiphersList
38 | NeedCiphersList
38 | NeedEmail
39 | NeedEmail
39 | NeedsReset
40 | NeedsReset
41+ | Open String
40 | RequestCipher String
42 | RequestCipher String
41 | SendMasterPassword String
43 | SendMasterPassword String
42
44
43jsonDecCmd : Json.Decode.Decoder ( Cmd )
45jsonDecCmd : Json.Decode.Decoder ( Cmd )
44jsonDecCmd =
46jsonDecCmd =
45 let jsonDecDictCmd = Dict.fromList
47 let jsonDecDictCmd = Dict.fromList
48+ [ ("Copy", Json.Decode.lazy (\_ -> Json.Decode.map Copy (Json.Decode.string)))
46− [ ("Init", Json.Decode.lazy (\_ -> Json.Decode.succeed Init))
49+ , ("Init", Json.Decode.lazy (\_ -> Json.Decode.succeed Init))
47 , ("Login", Json.Decode.lazy (\_ -> Json.Decode.map Login (jsonDecCmd_Login)))
50 , ("Login", Json.Decode.lazy (\_ -> Json.Decode.map Login (jsonDecCmd_Login)))
48 , ("NeedCiphersList", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedCiphersList))
51 , ("NeedCiphersList", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedCiphersList))
49 , ("NeedEmail", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedEmail))
52 , ("NeedEmail", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedEmail))
50 , ("NeedsReset", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsReset))
53 , ("NeedsReset", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsReset))
54+ , ("Open", Json.Decode.lazy (\_ -> Json.Decode.map Open (Json.Decode.string)))
51 , ("RequestCipher", Json.Decode.lazy (\_ -> Json.Decode.map RequestCipher (Json.Decode.string)))
55 , ("RequestCipher", Json.Decode.lazy (\_ -> Json.Decode.map RequestCipher (Json.Decode.string)))
52 , ("SendMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map SendMasterPassword (Json.Decode.string)))
56 , ("SendMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map SendMasterPassword (Json.Decode.string)))
53 ]
57 ]
@@ -57,11 +61,13 @@ jsonDecCmd =
57jsonEncCmd : Cmd -> Value
61jsonEncCmd : Cmd -> Value
58jsonEncCmd val =
62jsonEncCmd val =
59 let keyval v = case v of
63 let keyval v = case v of
64+ Copy v1 -> ("Copy", encodeValue (Json.Encode.string v1))
60 Init -> ("Init", encodeValue (Json.Encode.list identity []))
65 Init -> ("Init", encodeValue (Json.Encode.list identity []))
61 Login v1 -> ("Login", encodeValue (jsonEncCmd_Login v1))
66 Login v1 -> ("Login", encodeValue (jsonEncCmd_Login v1))
62 NeedCiphersList -> ("NeedCiphersList", encodeValue (Json.Encode.list identity []))
67 NeedCiphersList -> ("NeedCiphersList", encodeValue (Json.Encode.list identity []))
63 NeedEmail -> ("NeedEmail", encodeValue (Json.Encode.list identity []))
68 NeedEmail -> ("NeedEmail", encodeValue (Json.Encode.list identity []))
64 NeedsReset -> ("NeedsReset", encodeValue (Json.Encode.list identity []))
69 NeedsReset -> ("NeedsReset", encodeValue (Json.Encode.list identity []))
70+ Open v1 -> ("Open", encodeValue (Json.Encode.string v1))
65 RequestCipher v1 -> ("RequestCipher", encodeValue (Json.Encode.string v1))
71 RequestCipher v1 -> ("RequestCipher", encodeValue (Json.Encode.string v1))
66 SendMasterPassword v1 -> ("SendMasterPassword", encodeValue (Json.Encode.string v1))
72 SendMasterPassword v1 -> ("SendMasterPassword", encodeValue (Json.Encode.string v1))
67 in encodeSumTaggedObject "tag" "contents" keyval val
73 in encodeSumTaggedObject "tag" "contents" keyval val
frontend/Main.elm+10−2
@@ -36,6 +36,8 @@ type Msg
36 | RequestCipher CipherId
36 | RequestCipher CipherId
37 | PopView
37 | PopView
38 | RecieveEmail String
38 | RecieveEmail String
39+ | Copy String
40+ | Open String
39
41
40
42
41type alias CipherId =
43type alias CipherId =
@@ -331,6 +333,12 @@ update msg model =
331 RecieveEmail email ->
333 RecieveEmail email ->
332 ( { model | userEmail = Just email }, Cmd.none )
334 ( { model | userEmail = Just email }, Cmd.none )
333
335
336+ Copy text ->
337+ ( model, FFI.sendBridge (Bridge.Copy text) )
338+
339+ Open uri ->
340+ ( model, FFI.sendBridge (Bridge.Open uri) )
341+
334
342
335loginCallbacks : Login.Callbacks Msg
343loginCallbacks : Login.Callbacks Msg
336loginCallbacks =
344loginCallbacks =
@@ -342,9 +350,9 @@ ciphersCallbacks =
342 { selected = RequestCipher, logOut = NeedsReset }
350 { selected = RequestCipher, logOut = NeedsReset }
343
351
344
352
345−cipherCallbacks : Cipher.Callbacks
353+cipherCallbacks : Cipher.Callbacks Msg
346cipherCallbacks =
354cipherCallbacks =
347− {}
355+ { copy = Copy, open = Open }
348
356
349
357
350masterPasswordCallbacks : MasterPassword.Callbacks Msg
358masterPasswordCallbacks : MasterPassword.Callbacks Msg
frontend/Pages/Cipher.elm+58−39
@@ -16,16 +16,17 @@ type alias Model =
16
16
17
17
18type Msg
18type Msg
19− = Noop
20− | TogglePasswordVisiblity
19+ = TogglePasswordVisiblity
21 | ToggleCVVVisiblity
20 | ToggleCVVVisiblity
21+ | Copy String
22+ | Open String
22
23
23
24
24−type alias Callbacks =
25+type alias Callbacks msg =
25− {}
26+ { copy : String -> msg, open : String -> msg }
26
27
27
28
28−page : Callbacks -> Page Bridge.Sub_LoadCipher Model Msg emsg
29+page : Callbacks emsg -> Page Bridge.Sub_LoadCipher Model Msg emsg
29page callbacks liftMsg =
30page callbacks liftMsg =
30 { init = \cipher -> Tuple.mapSecond (Cmd.map liftMsg) (init cipher)
31 { init = \cipher -> Tuple.mapSecond (Cmd.map liftMsg) (init cipher)
31 , view = \model -> view model |> List.map (Html.map liftMsg)
32 , view = \model -> view model |> List.map (Html.map liftMsg)
@@ -45,18 +46,21 @@ init cipher =
45 )
46 )
46
47
47
48
48−update : Callbacks -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
49+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
49−update {} _ msg model =
50+update { copy, open } _ msg model =
50 case msg of
51 case msg of
51− Noop ->
52− ( Ok model, Cmd.none )
53−
54 TogglePasswordVisiblity ->
52 TogglePasswordVisiblity ->
55 ( Ok { model | passwordHidden = not model.passwordHidden }, Cmd.none )
53 ( Ok { model | passwordHidden = not model.passwordHidden }, Cmd.none )
56
54
57 ToggleCVVVisiblity ->
55 ToggleCVVVisiblity ->
58 ( Ok { model | cvvHidden = not model.cvvHidden }, Cmd.none )
56 ( Ok { model | cvvHidden = not model.cvvHidden }, Cmd.none )
59
57
58+ Copy text ->
59+ ( Ok model, copy text |> pureCmd )
60+
61+ Open uri ->
62+ ( Ok model, open uri |> pureCmd )
63+
60
64
61subscriptions : Model -> Sub Msg
65subscriptions : Model -> Sub Msg
62subscriptions _ =
66subscriptions _ =
@@ -80,7 +84,7 @@ view { passwordHidden, cipher, cvvHidden } =
80 maybeList username
84 maybeList username
81 (\x ->
85 (\x ->
82 row
86 row
83− { name = "Username", value = x, nameIcon = "user", icons = [ ( "copy", Noop ) ] }
87+ { name = "Username", value = x, nameIcon = "user", icons = [ ( "copy", Copy x ) ] }
84 )
88 )
85 ++ maybeList password
89 ++ maybeList password
86 (\x ->
90 (\x ->
@@ -93,14 +97,14 @@ view { passwordHidden, cipher, cvvHidden } =
93 else
97 else
94 x
98 x
95 , nameIcon = "security"
99 , nameIcon = "security"
96− , icons = [ ( hiddenButtonIcon passwordHidden, TogglePasswordVisiblity ), ( "copy", Noop ) ]
100+ , icons = [ ( hiddenButtonIcon passwordHidden, TogglePasswordVisiblity ), ( "copy", Copy x ) ]
97 }
101 }
98 )
102 )
99 ++ (uris
103 ++ (uris
100 |> List.map
104 |> List.map
101 (\uri ->
105 (\uri ->
102 row
106 row
103− { name = "URL", value = uri, nameIcon = "get-link", icons = [ ( "external-link", Noop ), ( "copy", Noop ) ] }
107+ { name = "URL", value = uri, nameIcon = "get-link", icons = [ ( "external-link", Open uri ), ( "copy", Copy uri ) ] }
104 )
108 )
105 )
109 )
106
110
@@ -109,14 +113,19 @@ view { passwordHidden, cipher, cvvHidden } =
109 ]
113 ]
110
114
111 Bridge.CardCipher { number, code, cardholderName, expMonth, expYear } ->
115 Bridge.CardCipher { number, code, cardholderName, expMonth, expYear } ->
112− maybeList number (\x -> row { name = "Card number", value = x, nameIcon = "containers", icons = [ ( "copy", Noop ) ] })
116+ maybeList number (\x -> row { name = "Card number", value = x, nameIcon = "containers", icons = [ ( "copy", Copy x ) ] })
113 ++ optional (expMonth /= Nothing || expYear /= Nothing)
117 ++ optional (expMonth /= Nothing || expYear /= Nothing)
114 (row
118 (row
115− { name = "Expiration date"
119+ (let
120+ x =
116− , value = [ expMonth, expYear ] |> List.concatMap (flip maybeList identity) |> String.join "/"
121+ [ expMonth, expYear ] |> List.concatMap (flip maybeList identity) |> String.join "/"
122+ in
123+ { name = "Expiration date"
124+ , value = x
117− , nameIcon = "timed-out"
125+ , nameIcon = "timed-out"
118− , icons = [ ( "copy", Noop ) ]
126+ , icons = [ ( "copy", Copy x ) ]
119− }
127+ }
128+ )
120 )
129 )
121 ++ maybeList code
130 ++ maybeList code
122 (\x ->
131 (\x ->
@@ -129,20 +138,25 @@ view { passwordHidden, cipher, cvvHidden } =
129 else
138 else
130 x
139 x
131 , nameIcon = "security"
140 , nameIcon = "security"
132− , icons = [ ( hiddenButtonIcon cvvHidden, ToggleCVVVisiblity ), ( "copy", Noop ) ]
141+ , icons = [ ( hiddenButtonIcon cvvHidden, ToggleCVVVisiblity ), ( "copy", Copy x ) ]
133 }
142 }
134 )
143 )
135 ++ maybeList cardholderName
144 ++ maybeList cardholderName
136− (\x -> row { name = "Cardholder name", value = x, nameIcon = "user", icons = [ ( "copy", Noop ) ] })
145+ (\x -> row { name = "Cardholder name", value = x, nameIcon = "user", icons = [ ( "copy", Copy x ) ] })
137
146
138− Bridge.IdentityCipher { address1, address2, address3, city, company, country, email, firstName, lastName, licenseNumber, middleName, passportNumber, phone, postalCode, ssn, state, title, username } ->
147+ Bridge.IdentityCipher { address1, address2, address3, company, email, firstName, lastName, licenseNumber, middleName, passportNumber, phone, ssn, username } ->
139 optional (firstName /= Nothing || middleName /= Nothing || lastName /= Nothing)
148 optional (firstName /= Nothing || middleName /= Nothing || lastName /= Nothing)
140 (row
149 (row
141− { name = "Name"
150+ (let
151+ x =
142− , value = [ firstName, middleName, lastName ] |> List.concatMap (flip maybeList identity) |> unwords
152+ [ firstName, middleName, lastName ] |> List.concatMap (flip maybeList identity) |> unwords
153+ in
154+ { name = "Name"
155+ , value = x
143− , nameIcon = ""
156+ , nameIcon = ""
144− , icons = [ ( "copy", Noop ) ]
157+ , icons = [ ( "copy", Copy x ) ]
145− }
158+ }
159+ )
146 )
160 )
147 ++ maybeList username
161 ++ maybeList username
148 (\x ->
162 (\x ->
@@ -150,7 +164,7 @@ view { passwordHidden, cipher, cvvHidden } =
150 { name = "Username"
164 { name = "Username"
151 , value = x
165 , value = x
152 , nameIcon = ""
166 , nameIcon = ""
153− , icons = [ ( "copy", Noop ) ]
167+ , icons = [ ( "copy", Copy x ) ]
154 }
168 }
155 )
169 )
156 ++ maybeList company
170 ++ maybeList company
@@ -159,7 +173,7 @@ view { passwordHidden, cipher, cvvHidden } =
159 { name = "Company"
173 { name = "Company"
160 , value = x
174 , value = x
161 , nameIcon = ""
175 , nameIcon = ""
162− , icons = [ ( "copy", Noop ) ]
176+ , icons = [ ( "copy", Copy x ) ]
163 }
177 }
164 )
178 )
165 ++ maybeList ssn
179 ++ maybeList ssn
@@ -168,7 +182,7 @@ view { passwordHidden, cipher, cvvHidden } =
168 { name = "Socia security number"
182 { name = "Socia security number"
169 , value = x
183 , value = x
170 , nameIcon = ""
184 , nameIcon = ""
171− , icons = [ ( "copy", Noop ) ]
185+ , icons = [ ( "copy", Copy x ) ]
172 }
186 }
173 )
187 )
174 ++ maybeList passportNumber
188 ++ maybeList passportNumber
@@ -177,7 +191,7 @@ view { passwordHidden, cipher, cvvHidden } =
177 { name = "Passport number"
191 { name = "Passport number"
178 , value = x
192 , value = x
179 , nameIcon = ""
193 , nameIcon = ""
180− , icons = [ ( "copy", Noop ) ]
194+ , icons = [ ( "copy", Copy x ) ]
181 }
195 }
182 )
196 )
183 ++ maybeList licenseNumber
197 ++ maybeList licenseNumber
@@ -186,7 +200,7 @@ view { passwordHidden, cipher, cvvHidden } =
186 { name = "License Number"
200 { name = "License Number"
187 , value = x
201 , value = x
188 , nameIcon = ""
202 , nameIcon = ""
189− , icons = [ ( "copy", Noop ) ]
203+ , icons = [ ( "copy", Copy x ) ]
190 }
204 }
191 )
205 )
192 ++ maybeList email
206 ++ maybeList email
@@ -195,7 +209,7 @@ view { passwordHidden, cipher, cvvHidden } =
195 { name = "Email"
209 { name = "Email"
196 , value = x
210 , value = x
197 , nameIcon = ""
211 , nameIcon = ""
198− , icons = [ ( "copy", Noop ) ]
212+ , icons = [ ( "copy", Copy x ) ]
199 }
213 }
200 )
214 )
201 ++ maybeList phone
215 ++ maybeList phone
@@ -204,16 +218,21 @@ view { passwordHidden, cipher, cvvHidden } =
204 { name = "Phone number"
218 { name = "Phone number"
205 , value = x
219 , value = x
206 , nameIcon = ""
220 , nameIcon = ""
207− , icons = [ ( "copy", Noop ) ]
221+ , icons = [ ( "copy", Copy x ) ]
208 }
222 }
209 )
223 )
210 ++ optional (address1 /= Nothing || address2 /= Nothing || address3 /= Nothing)
224 ++ optional (address1 /= Nothing || address2 /= Nothing || address3 /= Nothing)
211 (row
225 (row
212− { name = "Address"
226+ (let
227+ x =
213− , value = [ address1, address2, address3 ] |> List.concatMap (flip maybeList identity) |> unwords
228+ [ address1, address2, address3 ] |> List.concatMap (flip maybeList identity) |> unwords
229+ in
230+ { name = "Address"
231+ , value = x
214− , nameIcon = ""
232+ , nameIcon = ""
215− , icons = [ ( "copy", Noop ) ]
233+ , icons = [ ( "copy", Copy x ) ]
216− }
234+ }
235+ )
217 )
236 )
218 )
237 )
219 |> List.intersperse (hr [] [])
238 |> List.intersperse (hr [] [])
packages.dhall−20
@@ -128,23 +128,3 @@ in upstream
128 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
128 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
129 , version = "purescript-0.14"
129 , version = "purescript-0.14"
130 }
130 }
131− with subtlecrypto =
132− { dependencies =
133− [ "aff"
134− , "aff-promise"
135− , "arraybuffer-types"
136− , "console"
137− , "effect"
138− , "either"
139− , "exceptions"
140− , "foreign"
141− , "functions"
142− , "maybe"
143− , "prelude"
144− , "transformers"
145− , "tuples"
146− , "unsafe-coerce"
147− ]
148− , repo = "https://github.com/xc-jp/purescript-subtlecrypto.git"
149− , version = "7999dc92b94d1b7285375cfaad4a0ae6644b71b6"
150− }
spago.dhall−4
@@ -29,21 +29,17 @@ to generate this file without the comments in this block.
29 , "el"
29 , "el"
30 , "exceptions"
30 , "exceptions"
31 , "foldable-traversable"
31 , "foldable-traversable"
32− , "foreign"
33 , "functions"
32 , "functions"
34− , "lists"
35 , "maybe"
33 , "maybe"
36 , "newtype"
34 , "newtype"
37 , "nullable"
35 , "nullable"
38 , "prelude"
36 , "prelude"
39 , "refs"
37 , "refs"
40 , "strings"
38 , "strings"
41− , "subtlecrypto"
42 , "transformers"
39 , "transformers"
43 , "tuples"
40 , "tuples"
44 , "typelevel-prelude"
41 , "typelevel-prelude"
45 , "undefined-is-not-a-problem"
42 , "undefined-is-not-a-problem"
46− , "unsafe-coerce"
47 , "web-html"
43 , "web-html"
48 , "web-storage"
44 , "web-storage"
49 ]
45 ]