on master help sign in
.github/FUNDING.yml+1−1
since splitsnapshots
unifiedsplit
@@ -1 +1 @@
1−custom: https://iko.soy/donate
1+custom: ["https://iko.soy/donate"]
.github/workflows/build.yaml+47−7
@@ -5,13 +5,21 @@ on:
5 branches: ["master"]
5 branches: ["master"]
6 pull_request:
6 pull_request:
7 branches: ["master"]
7 branches: ["master"]
8+ release:
9+ types: [prereleased]
8
10
9jobs:
11jobs:
10 build:
12 build:
11 runs-on: ubuntu-latest
13 runs-on: ubuntu-latest
12
14
13 steps:
15 steps:
14− - uses: cachix/install-nix-action@v18
16+ - uses: cachix/install-nix-action@v19
17+
18+ - name: Cachix
19+ uses: cachix/cachix-action@v12
20+ with:
21+ name: iko
22+ authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
15
23
16 - uses: actions/checkout@v3
24 - uses: actions/checkout@v3
17 with:
25 with:
@@ -19,20 +27,26 @@ jobs:
19
27
20 - name: Install clickable
28 - name: Install clickable
21 run: |
29 run: |
30+ docker --version
22 sudo apt install adb git python3 python3-pip python3-setuptools
31 sudo apt install adb git python3 python3-pip python3-setuptools
23− pip3 install --user clickable-ut --upgrade
32+ pip3 install --user clickable-ut==7.10.0 --upgrade
24 echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
33 echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
25
34
26− - name: Build bw libraries
35+ - name: Build artifacts
27− run: ./dev/build-bw.sh
36+ run: ./dev/build.sh
28
37
29 - name: Build Warden
38 - name: Build Warden
30− run: clickable build --output dist
39+ run: clickable build --output click
40+
41+ - uses: actions/upload-artifact@v3
42+ with:
43+ name: click
44+ path: click/*
31
45
32 - uses: actions/upload-artifact@v3
46 - uses: actions/upload-artifact@v3
33 with:
47 with:
34− name: warden
48+ name: dist
35− path: dist/*
49+ path: dist
36
50
37 # Tests
51 # Tests
38
52
@@ -55,3 +69,29 @@ jobs:
55 with:
69 with:
56 name: playwright-report
70 name: playwright-report
57 path: tests/playwright-report/
71 path: tests/playwright-report/
72+
73+ # Upload release
74+
75+ - name: Zip Release Assets
76+ if: github.event_name == 'release'
77+ shell: bash
78+ run: |
79+ cd dist/
80+ zip -r ../../../../dist.zip .
81+
82+ - name: Upload dist to release
83+ if: github.event_name == 'release'
84+ uses: svenstaro/upload-release-action@2.4.1
85+ with:
86+ repo_token: ${{ secrets.GITHUB_TOKEN }}
87+ file: dist.zip
88+ tag: ${{ github.ref }}
89+
90+ - name: Upload click to release
91+ if: github.event_name == 'release'
92+ uses: svenstaro/upload-release-action@2.4.1
93+ with:
94+ repo_token: ${{ secrets.GITHUB_TOKEN }}
95+ file: build/all/app/*.click
96+ tag: ${{ github.ref }}
97+ file_glob: true
.gitignore+6−1
@@ -7,7 +7,6 @@ static/backend.js
7.spago
7.spago
8output
8output
9.psc-ide-port
9.psc-ide-port
10−backend.js
11elm.js
10elm.js
12output-es
11output-es
13elm-stuff
12elm-stuff
@@ -15,3 +14,9 @@ node_modules/
15/tests/test-results/
14/tests/test-results/
16/tests/playwright-report/
15/tests/playwright-report/
17/tests/playwright/.cache/
16/tests/playwright/.cache/
17+generated-docs
18+backend.js.map
19+.spago2nix
20+backend.js
21+vanilla
22+dist
.gitmodulesdeleted−6
@@ -1,6 +0,0 @@
1−[submodule "deps/bw"]
2− path = deps/bw
3− url = https://github.com/bitwarden/clients.git
4−[submodule "vanilla"]
5− path = vanilla
6− url = https://github.com/ilyakooo0/vanilla-compiled.git
.helix/languages.tomlnew file+3
@@ -0,0 +1,3 @@
1+[[language]]
2+name = "nix"
3+auto-format = true
backend-main.jsnew file+3
@@ -0,0 +1,3 @@
1+import * as Main from "./backend/Main/index.js"
2+
3+Main.main()
backend/BW.js+34−54
@@ -1,27 +1,29 @@
1−import { StateFactory } from "../../deps/bw/libs/shared/dist/src/factories/stateFactory";
1+import { StateFactory } from "../../bw/factories/stateFactory";
2−import { GlobalState } from "../../deps/bw/libs/shared/dist/src/models/domain/globalState";
2+import { GlobalState } from "../../bw/models/domain/global-state";
3−import { ApiService } from "../../deps/bw/libs/shared/dist/src/services/api.service";
3+import { ApiService } from "../../bw/services/api.service";
4−import { AppIdService } from "../../deps/bw/libs/shared/dist/src/services/appId.service";
4+import { AppIdService } from "../../bw/services/appId.service";
5−import { ConsoleLogService } from "../../deps/bw/libs/shared/dist/src/services/consoleLog.service";
5+import { ConsoleLogService } from "../../bw/services/consoleLog.service";
6−import { EnvironmentService } from "../../deps/bw/libs/shared/dist/src/services/environment.service";
6+import { EnvironmentService } from "../../bw/services/environment.service";
7−import { StateMigrationService } from "../../deps/bw/libs/shared/dist/src/services/stateMigration.service";
7+import { StateMigrationService } from "../../bw/services/stateMigration.service";
8−import { TokenService } from "../../deps/bw/libs/shared/dist/src/services/token.service";
8+import { TokenService } from "../../bw/services/token.service";
9−import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/services/webCryptoFunction.service";
9+import { WebCryptoFunctionService } from "../../bw/services/webCryptoFunction.service";
10−import { Account, AccountProfile, AccountTokens } from "../../deps/bw/libs/shared/dist/src/models/domain/account";
10+import { Account, AccountProfile, AccountTokens } from "../../bw/models/domain/account";
11−
11+import {MemoryStorageService} from "../../bw/services/memoryStorage.service"
12+
12−// import BrowserStorageService from "../../../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
13+// import BrowserStorageService from "../../../../bw/services/browserStorage.service";
13−import { CryptoService } from "../../deps/bw/libs/shared/dist/src/services/crypto.service";
14+import { CryptoService } from "../../bw/services/crypto.service";
14−
15+
15−import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
16+import { I18nService } from "../../bw/services/i18n.service";
16−import { StateService as BaseStateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
17+import { StateService as BaseStateService } from "../../bw/services/state.service";
17−import { PasswordTokenRequest } from "../../deps/bw/libs/shared/dist/src/models/request/identityToken/passwordTokenRequest";
18+import { PasswordTokenRequest } from "../../bw/models/request/identity-token/password-token.request";
18−import { TokenRequestTwoFactor } from "../../deps/bw/libs/common/src/models/request/identityToken/tokenRequestTwoFactor";
19+import { TokenTwoFactorRequest} from "../../bw/models/request/identity-token/token-two-factor.request";
19−import { CipherRequest } from "../../deps/bw/libs/common/src/models/request/cipherRequest";
20+import { CipherRequest } from "../../bw/vault/models/request/cipher.request";
20−import { CipherCreateRequest } from "../../deps/bw/libs/common/src/models/request/cipherCreateRequest";
21+import { CipherCreateRequest } from "../../bw/vault/models/request/cipher-create.request";
21−import { Cipher } from "../../deps/bw/libs/common/src/models/domain/cipher";
22+import { Cipher } from "../../bw/vault/models/domain/cipher";
22−import { CipherData } from "../../deps/bw/libs/common/src/models/data/cipherData";
23+import { CipherData } from "../../bw/vault/models/data/cipher.data";
23−import { PasswordGenerationService } from "../../deps/bw/libs/common/src/services/passwordGeneration.service";
24+import { PasswordGenerationService } from "../../bw/services/passwordGeneration.service";
24−import { TotpService } from "../../deps/bw/libs/shared/dist/src/services/totp.service"
25+import { TotpService } from "../../bw/services/totp.service"
26+import { EncryptServiceImplementation } from "../../bw/services/cryptography/encrypt.service.implementation"
25
27
26function sanitize(obj) {
28function sanitize(obj) {
27 return JSON.parse(JSON.stringify(obj));
29 return JSON.parse(JSON.stringify(obj));
@@ -73,7 +75,7 @@ export function getServices() {
73 req.email,
75 req.email,
74 req.masterPasswordHash,
76 req.masterPasswordHash,
75 req.captchaResponse,
77 req.captchaResponse,
76− new TokenRequestTwoFactor(
78+ new TokenTwoFactorRequest(
77 req.twoFactor.provider,
79 req.twoFactor.provider,
78 req.twoFactor.token,
80 req.twoFactor.token,
79 req.twoFactor.remember
81 req.twoFactor.remember
@@ -131,6 +133,7 @@ class MainBackground {
131 new StateFactory(GlobalState, Account)
133 new StateFactory(GlobalState, Account)
132 );
134 );
133 this.stateService = new StateService(
135 this.stateService = new StateService(
136+ this.storageService,
134 this.storageService,
137 this.storageService,
135 this.storageService,
138 this.storageService,
136 this.logService,
139 this.logService,
@@ -161,8 +164,13 @@ class MainBackground {
161 );
164 );
162 this.i18nService = new I18nService("en");
165 this.i18nService = new I18nService("en");
163 this.cryptoFunctionService = new WebCryptoFunctionService(window);
166 this.cryptoFunctionService = new WebCryptoFunctionService(window);
167+ this.encryptService = new EncryptServiceImplementation(
168+ this.cryptoFunctionService,
169+ this.logService
170+ );
164 this.cryptoService = new BrowserCryptoService(
171 this.cryptoService = new BrowserCryptoService(
165 this.cryptoFunctionService,
172 this.cryptoFunctionService,
173+ this.encryptService,
166 this.platformUtilsService,
174 this.platformUtilsService,
167 this.logService,
175 this.logService,
168 this.stateService
176 this.stateService
@@ -206,34 +214,6 @@ class MainBackground {
206 }
214 }
207}
215}
208
216
209−class MemoryStorageService {
210−
211− constructor() {
212− this.storage = {};
213− }
214−
215− async get(key) {
216− return this.storage[key]
217− }
218−
219− async has(key) {
220− return key in this.storage;
221− }
222−
223− async save(key, obj) {
224− if (obj == null) {
225− this.remove(key)
226− return
227− }
228−
229− this.storage[key] = obj
230− }
231−
232− async remove(key) {
233− delete this.storage[key]
234− }
235−}
236−
237class BrowserCryptoService extends CryptoService {
217class BrowserCryptoService extends CryptoService {
238 async retrieveKeyFromStorage(keySuffix) {
218 async retrieveKeyFromStorage(keySuffix) {
239 if (keySuffix === "biometric") {
219 if (keySuffix === "biometric") {
@@ -314,7 +294,7 @@ class BrowserPlatformUtilsService {
314 saveFile(win, blobData, blobOptions, fileName) {
294 saveFile(win, blobData, blobOptions, fileName) {
315 // BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
295 // BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
316 }
296 }
317− getApplicationVersion() {
297+ getApplicationVersionNumber() {
318 // return Promise.resolve(BrowserApi.getApplicationVersion());
298 // return Promise.resolve(BrowserApi.getApplicationVersion());
319 }
299 }
320 supportsWebAuthn(win) {
300 supportsWebAuthn(win) {
backend/BW.purs+37−39
@@ -17,31 +17,31 @@ import Effect (Effect)
17import Effect.Unsafe (unsafePerformEffect)
17import Effect.Unsafe (unsafePerformEffect)
18import Untagged.Union (type (|+|))
18import Untagged.Union (type (|+|))
19
19
20−type TotpService
20+type TotpService =
21− = { getCode :: String -> Promise String
21+ { getCode :: String -> Promise String
22− , getTimeInterval :: String -> Int
22+ , getTimeInterval :: String -> Int
23− }
23+ }
24−
24+
25−type CryptoService
25+type CryptoService =
26− = { makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey)
26+ { makeKey :: Fn4 Password String KDF KdfConfig (Promise SymmetricCryptoKey)
27− , hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
27+ , hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
28− , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
28+ , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
29− , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
29+ , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
30− , encrypt :: Fn2 String SymmetricCryptoKey (Promise EncString)
30+ , encrypt :: Fn2 String SymmetricCryptoKey (Promise EncString)
31− , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
31+ , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
32− }
32+ }
33−
33+
34−type ApiService
34+type ApiService =
35− = { postPrelogin :: PreloginRequest -> Promise PreloginResponse
35+ { postPrelogin :: PreloginRequest -> Promise PreloginResponse
36− , getProfile :: Unit -> Promise ProfileResponse
36+ , getProfile :: Unit -> Promise ProfileResponse
37− -- TODO: There can really be more request and response types. Maybe handle this later.
37+ -- TODO: There can really be more request and response types. Maybe handle this later.
38− , postIdentityToken :: PasswordTokenRequest -> Promise (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
38+ , postIdentityToken :: PasswordTokenRequest -> Promise (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
39− , getSync :: Unit -> Promise SyncResponse
39+ , getSync :: Unit -> Promise SyncResponse
40− , postCipherCreate :: CipherResponse -> Promise CipherResponse
40+ , postCipherCreate :: CipherResponse -> Promise CipherResponse
41− , putCipher :: CipherResponse -> Promise CipherResponse
41+ , putCipher :: CipherResponse -> Promise CipherResponse
42− , deleteCipher :: String -> Promise Unit
42+ , deleteCipher :: String -> Promise Unit
43− , postTwoFactorEmail :: TwoFactorEmailRequest -> Promise Unit
43+ , postTwoFactorEmail :: TwoFactorEmailRequest -> Promise Unit
44− }
44+ }
45
45
46type PasswordGeneration =
46type PasswordGeneration =
47 { generatePassword :: Bridge.PasswordGeneratorConfig -> Promise String
47 { generatePassword :: Bridge.PasswordGeneratorConfig -> Promise String
@@ -55,15 +55,13 @@ cryptoFunctionsTypeSha512 = "sha512" :: CryptoFunctionsType
55
55
56cryptoFunctionsTypeMd5 = "md5" :: CryptoFunctionsType
56cryptoFunctionsTypeMd5 = "md5" :: CryptoFunctionsType
57
57
58−type CryptoFunctionsType
58+type CryptoFunctionsType = String
59− = String
60
59
61−type CryptoFunctions
60+type CryptoFunctions =
62− = { hash :: Fn2 String CryptoFunctionsType (Promise Hash)
61+ { hash :: Fn2 String CryptoFunctionsType (Promise Hash)
63− }
62+ }
64
63
65−newtype Hash
66− = Hash (ArrayBuffer)
64+newtype Hash = Hash (ArrayBuffer)
67
65
68hashToArray :: Hash -> Array Int
66hashToArray :: Hash -> Array Int
69hashToArray (Hash buf) =
67hashToArray (Hash buf) =
@@ -86,12 +84,12 @@ instance EncodeJson Hash where
86instance DecodeJson Hash where
84instance DecodeJson Hash where
87 decodeJson = decodeJson >>> map arrayToHash
85 decodeJson = decodeJson >>> map arrayToHash
88
86
89−type Services
87+type Services =
90− = { crypto :: CryptoService
88+ { crypto :: CryptoService
91− , getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
89+ , getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
92− , cryptoFunctions :: CryptoFunctions
90+ , cryptoFunctions :: CryptoFunctions
93− , passwordGeneration :: PasswordGeneration
91+ , passwordGeneration :: PasswordGeneration
94− , totpService :: TotpService
92+ , totpService :: TotpService
95− }
93+ }
96
94
97foreign import getServices :: Effect Services
95foreign import getServices :: Effect Services
backend/BW/Logic.purs+294−248
@@ -3,6 +3,7 @@ module BW.Logic where
3import BW
3import BW
4import BW.Types
4import BW.Types
5import Prelude
5import Prelude
6+
6import Bridge as Bridge
7import Bridge as Bridge
7import Control.Monad.Error.Class (throwError)
8import Control.Monad.Error.Class (throwError)
8import Control.Promise (Promise)
9import Control.Promise (Promise)
@@ -14,6 +15,7 @@ import Data.JNullable as JNullable
14import Data.JOpt (JOpt(..), fromJOpt)
15import Data.JOpt (JOpt(..), fromJOpt)
15import Data.Maybe (Maybe(..), fromMaybe, maybe)
16import Data.Maybe (Maybe(..), fromMaybe, maybe)
16import Data.Newtype (class Newtype, unwrap, wrap)
17import Data.Newtype (class Newtype, unwrap, wrap)
18+import Data.Number.Format as Number
17import Data.String as String
19import Data.String as String
18import Data.SymmetricCryptoKey (SymmetricCryptoKey)
20import Data.SymmetricCryptoKey (SymmetricCryptoKey)
19import Data.SymmetricCryptoKey as SymmetricCryptoKey
21import Data.SymmetricCryptoKey as SymmetricCryptoKey
@@ -25,43 +27,51 @@ import Effect.Aff.Class (class MonadAff, liftAff)
25import Effect.Class (liftEffect)
27import Effect.Class (liftEffect)
26import Effect.Exception (error)
28import Effect.Exception (error)
27import Effect.Exception as Exc
29import Effect.Exception as Exc
30+import Effect.Random (random)
31+import Localstorage as Storage
28import Run (Run)
32import Run (Run)
29import Run.Reader (Reader, askAt)
33import Run.Reader (Reader, askAt)
34+import Storage (DeviceIDKey(..))
30import Type.Prelude (Proxy(..))
35import Type.Prelude (Proxy(..))
31import Untagged.Union (type (|+|))
36import Untagged.Union (type (|+|))
37+import Web.Storage.Storage (Storage)
32
38
33-- | Creates the master key
39-- | Creates the master key
34−makePreloginKey ::
40+makePreloginKey
35− forall r.
41+ :: forall r
36− PreloginResponse ->
42+ . PreloginResponse
37− Email ->
43+ -> Email
38− Password ->
44+ -> Password
39− Run
45+ -> Run
40− ( crypto ::
41− Reader CryptoService
46+ ( crypto :: Reader CryptoService
42− , effect :: Effect
47+ , effect :: Effect
43− , aff :: Aff
48+ , aff :: Aff
44− | r
49+ | r
45− )
50+ )
46− SymmetricCryptoKey
51+ SymmetricCryptoKey
47−makePreloginKey { kdf, kdfIterations } (Email email') password = do
52+makePreloginKey { kdf, kdfIterations, kdfMemory, kdfParallelism } (Email email') password = do
48 crypto <- askAt (Proxy :: _ "crypto")
53 crypto <- askAt (Proxy :: _ "crypto")
49 let
54 let
50 email = (String.trim >>> String.toLower) email'
55 email = (String.trim >>> String.toLower) email'
51− liftPromise $ runFn4 crypto.makeKey password email kdf kdfIterations
56+ liftPromise $ runFn4 crypto.makeKey password email kdf
57+ { iterations: kdfIterations
58+ , memory: kdfMemory
59+ , parallelism: kdfParallelism
60+ }
52
61
53−makeDecryptionKey ::
62+makeDecryptionKey
54− forall r.
63+ :: forall r
64+ .
55 -- | Matser key (prelogin key)
65 -- | Matser key (prelogin key)
56− SymmetricCryptoKey ->
66+ SymmetricCryptoKey
57− EncryptedString ->
67+ -> EncryptedString
58− Run
68+ -> Run
59− ( crypto :: Reader CryptoService
69+ ( crypto :: Reader CryptoService
60− , effect :: Effect
70+ , effect :: Effect
61− , aff :: Aff
71+ , aff :: Aff
62− | r
72+ | r
63− )
73+ )
64− SymmetricCryptoKey
74+ SymmetricCryptoKey
65makeDecryptionKey masterKey str = do
75makeDecryptionKey masterKey str = do
66 crypto <- askAt (Proxy :: _ "crypto")
76 crypto <- askAt (Proxy :: _ "crypto")
67 let
77 let
@@ -78,28 +88,32 @@ makeDecryptionKey masterKey str = do
78 liftEffect $ Exc.throw $ "Unsupported key encryption type: " <> show encType
88 liftEffect $ Exc.throw $ "Unsupported key encryption type: " <> show encType
79 pure $ SymmetricCryptoKey.fromArrayBuffer key
89 pure $ SymmetricCryptoKey.fromArrayBuffer key
80
90
81−getLogInRequestToken ::
91+getLogInRequestToken
82− forall r.
92+ :: forall r
83− { prelogin :: PreloginResponse
93+ . { prelogin :: PreloginResponse
84− , email :: Email
94+ , email :: Email
85− , password :: Password
95+ , password :: Password
86− , captchaResponse :: Maybe String
96+ , captchaResponse :: Maybe String
87− , secondFactor :: Bridge.Cmd_Login_secondFactor_Maybe
97+ , secondFactor :: Bridge.Cmd_Login_secondFactor_Maybe
88− } ->
98+ }
89− Run
99+ -> Run
90− ( api :: Reader ApiService
100+ ( api :: Reader ApiService
91− , crypto :: Reader CryptoService
101+ , crypto :: Reader CryptoService
92− , effect :: Effect
102+ , effect :: Effect
93− , aff :: Aff
103+ , aff :: Aff
104+ , storage :: Reader Storage
94− | r
105+ | r
95− )
106+ )
96− (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
107+ (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
97−getLogInRequestToken { prelogin
108+getLogInRequestToken
109+ { prelogin
98−, email
110+ , email
99−, password
111+ , password
100−, captchaResponse
112+ , captchaResponse
101−, secondFactor: Bridge.Cmd_Login_secondFactor_Maybe secondFactor
113+ , secondFactor: Bridge.Cmd_Login_secondFactor_Maybe secondFactor
102−} = do
114+ } = do
115+ key <- makePreloginKey prelogin email password
116+ crypto <- askAt (Proxy :: _ "crypto")
103 let
117 let
104 twoFactor =
118 twoFactor =
105 maybe
119 maybe
@@ -111,7 +125,11 @@ getLogInRequestToken { prelogin
111 x { provider = bridgeToSecondFactorType provider }
125 x { provider = bridgeToSecondFactorType provider }
112 )
126 )
113 secondFactor
127 secondFactor
128+ _localHashedPassword <-
129+ liftPromise
130+ $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
114− StringHash hashedPassword <- bwPasswordStringHash prelogin email password
131+ StringHash hashedPassword <- liftPromise $ runFn3 crypto.hashPassword password key jnull
132+ deviceId <- getDeviceID
115 api :: ApiService <- askAt (Proxy :: _ "api")
133 api :: ApiService <- askAt (Proxy :: _ "api")
116 liftPromise
134 liftPromise
117 $ api.postIdentityToken
135 $ api.postIdentityToken
@@ -122,23 +140,23 @@ getLogInRequestToken { prelogin
122 , device:
140 , device:
123 { type: deviceTypeUnknownBrowser
141 { type: deviceTypeUnknownBrowser
124 , name: "Ubuntu Touch"
142 , name: "Ubuntu Touch"
125− , identifier: "42"
143+ , identifier: deviceId
126 , pushToken: jnull
144 , pushToken: jnull
127 }
145 }
128 }
146 }
129
147
130−bwPasswordStringHash ::
148+bwPasswordStringHash
131− forall r.
149+ :: forall r
132− PreloginResponse ->
150+ . PreloginResponse
133− Email ->
151+ -> Email
134− Password ->
152+ -> Password
135− Run
153+ -> Run
136− ( crypto :: Reader CryptoService
154+ ( crypto :: Reader CryptoService
137− , effect :: Effect
155+ , effect :: Effect
138− , aff :: Aff
156+ , aff :: Aff
139− | r
157+ | r
140− )
158+ )
141− StringHash
159+ StringHash
142bwPasswordStringHash prelogin email password = do
160bwPasswordStringHash prelogin email password = do
143 key <- makePreloginKey prelogin email password
161 key <- makePreloginKey prelogin email password
144 crypto <- askAt (Proxy :: _ "crypto")
162 crypto <- askAt (Proxy :: _ "crypto")
@@ -147,47 +165,47 @@ bwPasswordStringHash prelogin email password = do
147 $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
165 $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
148 liftPromise $ runFn3 crypto.hashPassword password key jnull
166 liftPromise $ runFn3 crypto.hashPassword password key jnull
149
167
150−hashPassword ::
168+hashPassword
151− forall r.
169+ :: forall r
152− Password ->
170+ . Password
153− Run
171+ -> Run
154− ( cryptoFunctions :: Reader CryptoFunctions
172+ ( cryptoFunctions :: Reader CryptoFunctions
155− , aff :: Aff
173+ , aff :: Aff
156− , effect :: Effect
174+ , effect :: Effect
157− | r
175+ | r
158− )
176+ )
159− Hash
177+ Hash
160hashPassword (Password password) = do
178hashPassword (Password password) = do
161 cryptoFunctions <- askAt (Proxy :: _ "cryptoFunctions")
179 cryptoFunctions <- askAt (Proxy :: _ "cryptoFunctions")
162 liftPromise $ runFn2 cryptoFunctions.hash password cryptoFunctionsTypeSha512
180 liftPromise $ runFn2 cryptoFunctions.hash password cryptoFunctionsTypeSha512
163
181
164−decrypt ::
182+decrypt
165− forall r.
183+ :: forall r
166− EncryptedString ->
184+ . EncryptedString
167− Run
185+ -> Run
168− ( key :: Reader SymmetricCryptoKey
186+ ( key :: Reader SymmetricCryptoKey
169− , crypto :: Reader CryptoService
187+ , crypto :: Reader CryptoService
170− , aff :: Aff
188+ , aff :: Aff
171− , effect :: Effect
189+ , effect :: Effect
172− | r
190+ | r
173− )
191+ )
174− String
192+ String
175decrypt input = do
193decrypt input = do
176 crypto <- askAt (Proxy :: _ "crypto")
194 crypto <- askAt (Proxy :: _ "crypto")
177 key <- askAt (Proxy :: _ "key")
195 key <- askAt (Proxy :: _ "key")
178 liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
196 liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
179
197
180−encrypt ::
198+encrypt
181− forall r.
199+ :: forall r
182− String ->
200+ . String
183− Run
201+ -> Run
184− ( key :: Reader SymmetricCryptoKey
202+ ( key :: Reader SymmetricCryptoKey
185− , crypto :: Reader CryptoService
203+ , crypto :: Reader CryptoService
186− , aff :: Aff
204+ , aff :: Aff
187− , effect :: Effect
205+ , effect :: Effect
188− | r
206+ | r
189− )
207+ )
190− EncryptedString
208+ EncryptedString
191encrypt input = do
209encrypt input = do
192 crypto <- askAt (Proxy :: _ "crypto")
210 crypto <- askAt (Proxy :: _ "crypto")
193 key <- askAt (Proxy :: _ "key")
211 key <- askAt (Proxy :: _ "key")
@@ -196,17 +214,17 @@ encrypt input = do
196liftPromise :: forall m a. MonadAff m => Promise a -> m a
214liftPromise :: forall m a. MonadAff m => Promise a -> m a
197liftPromise = liftAff <<< Promise.toAff
215liftPromise = liftAff <<< Promise.toAff
198
216
199−encodeCipher ::
217+encodeCipher
200− forall r.
218+ :: forall r
201− Bridge.FullCipher ->
219+ . Bridge.FullCipher
202− Run
220+ -> Run
203− ( key :: Reader SymmetricCryptoKey
221+ ( key :: Reader SymmetricCryptoKey
204− , crypto :: Reader CryptoService
222+ , crypto :: Reader CryptoService
205− , aff :: Aff
223+ , aff :: Aff
206− , effect :: Effect
224+ , effect :: Effect
207− | r
225+ | r
208− )
226+ )
209− CipherResponse
227+ CipherResponse
210encodeCipher
228encodeCipher
211 (Bridge.FullCipher { name, cipher, id, favorite, reprompt, collectionIds }) = do
229 (Bridge.FullCipher { name, cipher, id, favorite, reprompt, collectionIds }) = do
212 let
230 let
@@ -222,14 +240,14 @@ encodeCipher
222 pure
240 pure
223 x
241 x
224 { card =
242 { card =
225− nullify
243+ nullify
226− { cardholderName
244+ { cardholderName
227− , brand
245+ , brand
228− , number
246+ , number
229− , expMonth
247+ , expMonth
230− , expYear
248+ , expYear
231− , code
249+ , code
232− }
250+ }
233 , type = cipherTypeCard
251 , type = cipherTypeCard
234 }
252 }
235 Bridge.IdentityCipher (Bridge.Cipher_IdentityCipher identity) -> do
253 Bridge.IdentityCipher (Bridge.Cipher_IdentityCipher identity) -> do
@@ -254,26 +272,26 @@ encodeCipher
254 pure
272 pure
255 x
273 x
256 { identity =
274 { identity =
257− nullify
275+ nullify
258− { address1
276+ { address1
259− , address2
277+ , address2
260− , address3
278+ , address3
261− , city
279+ , city
262− , company
280+ , company
263− , country
281+ , country
264− , email
282+ , email
265− , firstName
283+ , firstName
266− , lastName
284+ , lastName
267− , licenseNumber
285+ , licenseNumber
268− , middleName
286+ , middleName
269− , passportNumber
287+ , passportNumber
270− , phone
288+ , phone
271− , postalCode
289+ , postalCode
272− , ssn
290+ , ssn
273− , state
291+ , state
274− , title
292+ , title
275− , username
293+ , username
276− }
294+ }
277 , type = cipherTypeIdentity
295 , type = cipherTypeIdentity
278 }
296 }
279 Bridge.LoginCipher (Bridge.Cipher_LoginCipher login) -> do
297 Bridge.LoginCipher (Bridge.Cipher_LoginCipher login) -> do
@@ -281,7 +299,7 @@ encodeCipher
281 uris <-
299 uris <-
282 traverse
300 traverse
283 ( \uri -> do
301 ( \uri -> do
284− uriEnc <- encrypt uri
302+ uriEnc <- nullify <$> encrypt uri
285 pure { uri: uriEnc, match: jnull }
303 pure { uri: uriEnc, match: jnull }
286 )
304 )
287 (unwrap login.uris)
305 (unwrap login.uris)
@@ -290,14 +308,14 @@ encodeCipher
290 pure
308 pure
291 x
309 x
292 { login =
310 { login =
293− nullify
311+ nullify
294− { password
312+ { password
295− , uris: JOpt $ opt uris
313+ , uris: JOpt $ opt uris
296− , username
314+ , username
297− , passwordRevisionDate: jnull
315+ , passwordRevisionDate: jnull
298− , totp: totp
316+ , totp: totp
299− , autofillOnPageLoad: JOpt undefined
317+ , autofillOnPageLoad: JOpt undefined
300− }
318+ }
301 , type = cipherTypeLogin
319 , type = cipherTypeLogin
302 }
320 }
303 Bridge.NoteCipher note -> do
321 Bridge.NoteCipher note -> do
@@ -332,100 +350,110 @@ encodeCipher
332 , reprompt
350 , reprompt
333 }
351 }
334
352
335−decodeCipher ::
353+-- | Check if the cipher is supported by Warden.
354+supportedCipher :: CipherResponse -> Boolean
355+-- We do not support organizations since that requires managing a separate set of
356+-- encryption keys
357+supportedCipher { organizationId } | Just _ <- JNullable.toMaybe organizationId = false
358+supportedCipher _ = true
359+
360+decodeCipher
336− forall r.
361+ :: forall r
337− CipherResponse ->
362+ . CipherResponse
338− Run
363+ -> Run
339− ( key :: Reader SymmetricCryptoKey
364+ ( key :: Reader SymmetricCryptoKey
340− , crypto :: Reader CryptoService
365+ , crypto :: Reader CryptoService
341− , aff :: Aff
366+ , aff :: Aff
342− , effect :: Effect
367+ , effect :: Effect
343− | r
368+ | r
344− )
369+ )
345− Bridge.FullCipher
370+ (Maybe Bridge.FullCipher)
371+decodeCipher cipher | not (supportedCipher cipher) =
372+ pure $ Nothing
346decodeCipher cipher = do
373decodeCipher cipher = do
347 name <- decrypt cipher.name
374 name <- decrypt cipher.name
348 cipherType <- case cipher.type of
375 cipherType <- case cipher.type of
349 n
376 n
350 | cipherTypeSecureNote == n -> do
377 | cipherTypeSecureNote == n -> do
351− note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
378+ note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
352− pure $ Bridge.NoteCipher note
379+ pure $ Bridge.NoteCipher note
353 n
380 n
354 | cipherTypeLogin == n -> do
381 | cipherTypeLogin == n -> do
355− case JNullable.toMaybe cipher.login of
382+ case JNullable.toMaybe cipher.login of
356− Nothing -> liftEffect $ throwError $ error "Login data is missing"
383+ Nothing -> liftEffect $ throwError $ error "Login data is missing"
357− Just login -> do
384+ Just login -> do
358− username <- decryptNullable login.username
385+ username <- decryptNullable login.username
359− password <- decryptNullable login.password
386+ password <- decryptNullable login.password
360− totp <- decryptNullable login.totp
387+ totp <- decryptNullable login.totp
388+ uris <- fromJOpt [] <$>
361− uris <- fromJOpt [] <$> ((traverse >>> traverse) (_.uri >>> decrypt) login.uris)
389+ ((traverse >>> traverse) (_.uri >>> JNullable.toMaybe >>> maybe (pure "") decrypt) login.uris)
362− pure $ Bridge.LoginCipher
390+ pure $ Bridge.LoginCipher
363− $ Bridge.Cipher_LoginCipher
391+ $ Bridge.Cipher_LoginCipher
364− { username
392+ { username
365− , password
393+ , password
366− , uris: wrap uris
394+ , uris: wrap uris
367− , totp
395+ , totp
368− }
396+ }
369 n
397 n
370 | cipherTypeCard == n -> do
398 | cipherTypeCard == n -> do
371− case JNullable.toMaybe cipher.card of
399+ case JNullable.toMaybe cipher.card of
372− Nothing -> liftEffect $ throwError $ error "Card data is missing"
400+ Nothing -> liftEffect $ throwError $ error "Card data is missing"
373− Just card -> do
401+ Just card -> do
374− cardholderName <- decryptNullable card.cardholderName
402+ cardholderName <- decryptNullable card.cardholderName
375− number <- decryptNullable card.number
403+ number <- decryptNullable card.number
376− code <- decryptNullable card.code
404+ code <- decryptNullable card.code
377− brand <- decryptNullable card.brand
405+ brand <- decryptNullable card.brand
378− expMonth <- decryptNullable card.expMonth
406+ expMonth <- decryptNullable card.expMonth
379− expYear <- decryptNullable card.expYear
407+ expYear <- decryptNullable card.expYear
380− pure $ Bridge.CardCipher
408+ pure $ Bridge.CardCipher
381− $ Bridge.Cipher_CardCipher
409+ $ Bridge.Cipher_CardCipher
382− { brand, cardholderName, code, expMonth, expYear, number }
410+ { brand, cardholderName, code, expMonth, expYear, number }
383 n
411 n
384 | cipherTypeIdentity == n -> do
412 | cipherTypeIdentity == n -> do
385− case JNullable.toMaybe cipher.identity of
413+ case JNullable.toMaybe cipher.identity of
386− Nothing -> liftEffect $ throwError $ error "Identity data is missing"
414+ Nothing -> liftEffect $ throwError $ error "Identity data is missing"
387− Just identity -> do
415+ Just identity -> do
388− firstName <- decryptNullable identity.firstName
416+ firstName <- decryptNullable identity.firstName
389− middleName <- decryptNullable identity.middleName
417+ middleName <- decryptNullable identity.middleName
390− lastName <- decryptNullable identity.lastName
418+ lastName <- decryptNullable identity.lastName
391− address1 <- decryptNullable identity.address1
419+ address1 <- decryptNullable identity.address1
392− address2 <- decryptNullable identity.address2
420+ address2 <- decryptNullable identity.address2
393− address3 <- decryptNullable identity.address3
421+ address3 <- decryptNullable identity.address3
394− city <- decryptNullable identity.city
422+ city <- decryptNullable identity.city
395− company <- decryptNullable identity.company
423+ company <- decryptNullable identity.company
396− country <- decryptNullable identity.country
424+ country <- decryptNullable identity.country
397− email <- decryptNullable identity.email
425+ email <- decryptNullable identity.email
398− licenseNumber <- decryptNullable identity.licenseNumber
426+ licenseNumber <- decryptNullable identity.licenseNumber
399− passportNumber <- decryptNullable identity.passportNumber
427+ passportNumber <- decryptNullable identity.passportNumber
400− phone <- decryptNullable identity.phone
428+ phone <- decryptNullable identity.phone
401− postalCode <- decryptNullable identity.postalCode
429+ postalCode <- decryptNullable identity.postalCode
402− ssn <- decryptNullable identity.ssn
430+ ssn <- decryptNullable identity.ssn
403− state <- decryptNullable identity.state
431+ state <- decryptNullable identity.state
404− title <- decryptNullable identity.title
432+ title <- decryptNullable identity.title
405− username <- decryptNullable identity.username
433+ username <- decryptNullable identity.username
406− pure $ Bridge.IdentityCipher
434+ pure $ Bridge.IdentityCipher
407− $ wrap
435+ $ wrap
408− { firstName
436+ { firstName
409− , middleName
437+ , middleName
410− , lastName
438+ , lastName
411− , address1
439+ , address1
412− , address2
440+ , address2
413− , address3
441+ , address3
414− , city
442+ , city
415− , company
443+ , company
416− , country
444+ , country
417− , email
445+ , email
418− , licenseNumber
446+ , licenseNumber
419− , passportNumber
447+ , passportNumber
420− , phone
448+ , phone
421− , postalCode
449+ , postalCode
422− , ssn
450+ , ssn
423− , state
451+ , state
424− , title
452+ , title
425− , username
453+ , username
426− }
454+ }
427 n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
455 n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
428− pure
456+ pure $ Just
429 $ wrap
457 $ wrap
430 { cipher: cipherType
458 { cipher: cipherType
431 , name
459 , name
@@ -435,32 +463,50 @@ decodeCipher cipher = do
435 , collectionIds: wrap <<< fromJNullable [] $ cipher.collectionIds
463 , collectionIds: wrap <<< fromJNullable [] $ cipher.collectionIds
436 }
464 }
437
465
438−decryptNullable ::
466+decryptNullable
439− forall x r.
467+ :: forall x r
440− Newtype x (Maybe String) =>
468+ . Newtype x (Maybe String)
441− JNullable EncryptedString ->
469+ => JNullable EncryptedString
442− Run
470+ -> Run
443− ( key :: Reader SymmetricCryptoKey
471+ ( key :: Reader SymmetricCryptoKey
444− , crypto :: Reader CryptoService
472+ , crypto :: Reader CryptoService
445− , aff :: Aff
473+ , aff :: Aff
446− , effect :: Effect
474+ , effect :: Effect
447− | r
475+ | r
448− )
476+ )
449− x
477+ x
450decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
478decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
451
479
452−encryptNullable ::
480+encryptNullable
453− forall x r.
481+ :: forall x r
454− Newtype x (Maybe String) =>
482+ . Newtype x (Maybe String)
455− x ->
483+ => x
456− Run
484+ -> Run
457− ( key :: Reader SymmetricCryptoKey
485+ ( key :: Reader SymmetricCryptoKey
458− , crypto :: Reader CryptoService
486+ , crypto :: Reader CryptoService
459− , aff :: Aff
487+ , aff :: Aff
460− , effect :: Effect
488+ , effect :: Effect
461− | r
489+ | r
462− )
490+ )
463− (JNullable EncryptedString)
491+ (JNullable EncryptedString)
464encryptNullable x = case unwrap x of
492encryptNullable x = case unwrap x of
465 Nothing -> pure jnull
493 Nothing -> pure jnull
466 Just y -> nullify <$> encrypt y
494 Just y -> nullify <$> encrypt y
495+
496+getDeviceID ::
497+ forall r.
498+ Run
499+ ( effect :: Effect
500+ , storage :: Reader Storage
501+ | r
502+ )
503+ String
504+getDeviceID = do
505+ storage <- askAt (Proxy :: _ "storage")
506+ maybeId <- liftEffect $ Storage.get storage DeviceIDKey
507+ case maybeId of
508+ Just id -> pure id
509+ Nothing -> do
510+ id <- map Number.toString $ liftEffect $ random
511+ liftEffect $ Storage.store storage DeviceIDKey id
512+ pure id
backend/BW/Types.purs+392−396
@@ -4,22 +4,25 @@ import Prelude
4
4
5import Bridge as Bridge
5import Bridge as Bridge
6import Data.Argonaut (class DecodeJson, class EncodeJson)
6import Data.Argonaut (class DecodeJson, class EncodeJson)
7+import Data.Either (Either)
8+import Data.Either as Either
9+import Data.Int as Int
7import Data.JNullable (JNullable)
10import Data.JNullable (JNullable)
8import Data.JOpt (JOpt)
11import Data.JOpt (JOpt)
9import Data.Maybe (Maybe(..))
12import Data.Maybe (Maybe(..))
13+import Data.Nullable (Nullable)
10import Data.ShowableJson (ShowableJson)
14import Data.ShowableJson (ShowableJson)
11import Data.Timestamp (Timestamp)
15import Data.Timestamp (Timestamp)
12−import Data.Traversable (traverse)
16+import Data.Traversable (for)
13−import Foreign (Foreign)
17+import Data.Tuple.Nested ((/\))
14import Foreign as Foreign
18import Foreign as Foreign
15−import Foreign.Object (Object)
16import Literals.Undefined (Undefined)
19import Literals.Undefined (Undefined)
17import Type.Prelude (Proxy(..))
20import Type.Prelude (Proxy(..))
18−import Untagged.TypeCheck (class HasRuntimeType, hasRuntimeType)
21+import Untagged.TypeCheck (class HasRuntimeType)
19import Untagged.Union (type (|+|))
22import Untagged.Union (type (|+|))
23+import Untagged.Union as Union
20
24
21−newtype EncryptedString
25+newtype EncryptedString = EncryptedString String
22− = EncryptedString String
23
26
24derive newtype instance Show EncryptedString
27derive newtype instance Show EncryptedString
25derive newtype instance Ord EncryptedString
28derive newtype instance Ord EncryptedString
@@ -28,78 +31,86 @@ derive newtype instance EncodeJson EncryptedString
28derive newtype instance DecodeJson EncryptedString
31derive newtype instance DecodeJson EncryptedString
29
32
30kdfPBKDF2_SHA256 = 0 :: KDF
33kdfPBKDF2_SHA256 = 0 :: KDF
34+kdfArgon2id = 1 :: KDF
31
35
32−type KDF
36+type KDF = Int
33− = Int
37+
34−
35−type PreloginResponse
38+type PreloginResponse =
36− = { kdf :: KDF
39+ { kdf :: KDF
37− , kdfIterations :: Int
40+ , kdfIterations :: Int
41+ , kdfMemory :: JNullable Int
42+ , kdfParallelism :: JNullable Int
43+ }
44+
45+type KdfConfig =
46+ { iterations :: Int
47+ , memory :: JNullable Int
48+ , parallelism :: JNullable Int
38− }
49+ }
39−
50+
40−type PreloginRequest
51+type PreloginRequest =
41− = { email :: Email
52+ { email :: Email
42− }
53+ }
43−
54+
44−type ProfileResponse
55+type ProfileResponse =
45− = { id :: String
56+ { id :: String
46− , name :: JNullable String
57+ , name :: JNullable String
47− , email :: String
58+ , email :: String
48− , emailVerified :: Boolean
59+ , emailVerified :: Boolean
49− , masterPasswordHint :: JNullable String
60+ , masterPasswordHint :: JNullable String
50− , premiumPersonally :: Boolean
61+ -- , premiumPersonally :: Boolean
51− -- , premiumFromOrganization :: Boolean
62+ -- , premiumFromOrganization :: Boolean
52− , culture :: JNullable String
63+ , culture :: JNullable String
53− , twoFactorEnabled :: Boolean
64+ , twoFactorEnabled :: Boolean
54− , key :: EncryptedString
65+ , key :: EncryptedString
55− , privateKey :: EncryptedString
66+ , privateKey :: EncryptedString
56− , securityStamp :: JNullable String
67+ , securityStamp :: JNullable String
57− , forcePasswordReset :: Boolean
68+ , forcePasswordReset :: Boolean
58− , usesKeyConnector :: Boolean
69+ , usesKeyConnector :: Boolean
59− , organizations :: Array ProfileOrganizationResponse
70+ -- , organizations :: Array ProfileOrganizationResponse
60− , providers :: Array ProfileProviderResponse
71+ , providers :: Array ProfileProviderResponse
61− , providerOrganizations :: Array ProfileProviderOrganizationResponse
72+ , providerOrganizations :: Array ProfileProviderOrganizationResponse
62− }
73+ }
63−
74+
64−type ProfileOrganizationResponse
75+type ProfileOrganizationResponse =
65− = { id :: String
76+ { id :: String
66− , name :: JNullable String
77+ , name :: JNullable String
67− , usePolicies :: JNullable Boolean
78+ , usePolicies :: JNullable Boolean
68− , useGroups :: JNullable Boolean
79+ , useGroups :: JNullable Boolean
69− , useDirectory :: JNullable Boolean
80+ , useDirectory :: JNullable Boolean
70− , useEvents :: JNullable Boolean
81+ , useEvents :: JNullable Boolean
71− , useTotp :: JNullable Boolean
82+ , useTotp :: JNullable Boolean
72− , use2fa :: JNullable Boolean
83+ , use2fa :: JNullable Boolean
73− , useApi :: JNullable Boolean
84+ , useApi :: JNullable Boolean
74− , useSso :: JNullable Boolean
85+ , useSso :: JNullable Boolean
75− , useKeyConnector :: JNullable Boolean
86+ , useKeyConnector :: JNullable Boolean
76− , useResetPassword :: JNullable Boolean
87+ , useResetPassword :: JNullable Boolean
77− , selfHost :: JNullable Boolean
88+ , selfHost :: JNullable Boolean
78− , usersGetPremium :: JNullable Boolean
89+ , usersGetPremium :: JNullable Boolean
79− , seats :: JNullable Int
90+ , seats :: JNullable Int
80− , maxCollections :: JNullable Int
91+ , maxCollections :: JNullable Int
81− , maxStorageGb :: JNullable Int
92+ , maxStorageGb :: JNullable Int
82− , key :: JNullable String
93+ , key :: JNullable String
83− , hasPublicAndPrivateKeys :: JNullable Boolean
94+ , hasPublicAndPrivateKeys :: JNullable Boolean
84− , status :: JNullable OrganizationUserStatusType
95+ , status :: JNullable OrganizationUserStatusType
85− , type :: JNullable OrganizationUserType
96+ , type :: JNullable OrganizationUserType
86− , enabled :: JNullable Boolean
97+ , enabled :: JNullable Boolean
87− , ssoBound :: JNullable Boolean
98+ , ssoBound :: JNullable Boolean
88− , identifier :: JNullable String
99+ , identifier :: JNullable String
89− , permissions :: JNullable PermissionsApi
100+ , permissions :: JNullable PermissionsApi
90− , resetPasswordEnrolled :: JNullable Boolean
101+ , resetPasswordEnrolled :: JNullable Boolean
91− , userId :: JNullable String
102+ , userId :: JNullable String
92− , providerId :: JNullable String
103+ , providerId :: JNullable String
93− , providerName :: JNullable String
104+ , providerName :: JNullable String
94− , familySponsorshipFriendlyName :: JNullable String
105+ , familySponsorshipFriendlyName :: JNullable String
95− , familySponsorshipAvailable :: JNullable Boolean
106+ , familySponsorshipAvailable :: JNullable Boolean
96− , planProductType :: JNullable ProductType
107+ , planProductType :: JNullable ProductType
97− , keyConnectorEnabled :: JNullable Boolean
108+ , keyConnectorEnabled :: JNullable Boolean
98− , keyConnectorUrl :: JNullable String
109+ , keyConnectorUrl :: JNullable String
99− , familySponsorshipLastSyncDate :: JNullable Timestamp
110+ , familySponsorshipLastSyncDate :: JNullable Timestamp
100− , familySponsorshipValidUntil :: JNullable Timestamp
111+ , familySponsorshipValidUntil :: JNullable Timestamp
101− , familySponsorshipToDelete :: JNullable Boolean
112+ , familySponsorshipToDelete :: JNullable Boolean
102− }
113+ }
103
114
104organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
115organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
105
116
@@ -107,8 +118,7 @@ organizationUserStatusTypeAccepted = 0 :: OrganizationUserStatusType
107
118
108organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
119organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
109
120
110−type OrganizationUserStatusType
121+type OrganizationUserStatusType = Int
111− = Int
112
122
113organizationUserTypeOwner = 0 :: OrganizationUserType
123organizationUserTypeOwner = 0 :: OrganizationUserType
114
124
@@ -120,27 +130,26 @@ organizationUserTypeManager = 3 :: OrganizationUserType
120
130
121organizationUserTypeCustom = 4 :: OrganizationUserType
131organizationUserTypeCustom = 4 :: OrganizationUserType
122
132
123−type OrganizationUserType
133+type OrganizationUserType = Int
124− = Int
134+
125−
126−type PermissionsApi
135+type PermissionsApi =
127− = { accessEventLogs :: Boolean
136+ { accessEventLogs :: Boolean
128− , accessImportExport :: Boolean
137+ , accessImportExport :: Boolean
129− , accessReports :: Boolean
138+ , accessReports :: Boolean
130− , manageAllCollections :: Boolean
139+ , manageAllCollections :: Boolean
131− , createNewCollections :: Boolean
140+ , createNewCollections :: Boolean
132− , editAnyCollection :: Boolean
141+ , editAnyCollection :: Boolean
133− , deleteAnyCollection :: Boolean
142+ , deleteAnyCollection :: Boolean
134− , manageAssignedCollections :: Boolean
143+ , manageAssignedCollections :: Boolean
135− , editAssignedCollections :: Boolean
144+ , editAssignedCollections :: Boolean
136− , deleteAssignedCollections :: Boolean
145+ , deleteAssignedCollections :: Boolean
137− , manageCiphers :: Boolean
146+ , manageCiphers :: Boolean
138− , manageGroups :: Boolean
147+ , manageGroups :: Boolean
139− , manageSso :: Boolean
148+ , manageSso :: Boolean
140− , managePolicies :: Boolean
149+ , managePolicies :: Boolean
141− , manageUsers :: Boolean
150+ , manageUsers :: Boolean
142− , manageResetPassword :: Boolean
151+ , manageResetPassword :: Boolean
143− }
152+ }
144
153
145productTypeFree = 0 :: ProductType
154productTypeFree = 0 :: ProductType
146
155
@@ -150,20 +159,19 @@ productTypeTeams = 2 :: ProductType
150
159
151productTypeEnterprise = 3 :: ProductType
160productTypeEnterprise = 3 :: ProductType
152
161
153−type ProductType
162+type ProductType = Int
154− = Int
163+
155−
156−type ProfileProviderResponse
164+type ProfileProviderResponse =
157− = { id :: String
165+ { id :: String
158− , name :: JNullable String
166+ , name :: JNullable String
159− , key :: JNullable String
167+ , key :: JNullable String
160− , status :: JNullable ProviderUserStatusType
168+ , status :: JNullable ProviderUserStatusType
161− , type :: JNullable ProviderUserType
169+ , type :: JNullable ProviderUserType
162− , enabled :: Boolean
170+ , enabled :: Boolean
163− , permissions :: PermissionsApi
171+ , permissions :: PermissionsApi
164− , userId :: JNullable String
172+ , userId :: JNullable String
165− , useEvents :: Boolean
173+ , useEvents :: Boolean
166− }
174+ }
167
175
168providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
176providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
169
177
@@ -171,81 +179,79 @@ providerUserStatusTypeAccepted = 1 :: ProviderUserStatusType
171
179
172providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
180providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
173
181
174−type ProviderUserStatusType
182+type ProviderUserStatusType = Int
175− = Int
176
183
177providerUserTypeProviderAdmin = 0 :: ProviderUserType
184providerUserTypeProviderAdmin = 0 :: ProviderUserType
178
185
179providerUserTypeServiceUser = 1 :: ProviderUserType
186providerUserTypeServiceUser = 1 :: ProviderUserType
180
187
181−type ProviderUserType
188+type ProviderUserType = Int
182− = Int
189+
183−
184−type ProfileProviderOrganizationResponse
190+type ProfileProviderOrganizationResponse =
185− = { id :: String
191+ { id :: String
186− , name :: JNullable String
192+ , name :: JNullable String
187− , usePolicies :: Boolean
193+ , usePolicies :: Boolean
188− , useGroups :: Boolean
194+ , useGroups :: Boolean
189− , useDirectory :: Boolean
195+ , useDirectory :: Boolean
190− , useEvents :: Boolean
196+ , useEvents :: Boolean
191− , useTotp :: Boolean
197+ , useTotp :: Boolean
192− , use2fa :: Boolean
198+ , use2fa :: Boolean
193− , useApi :: Boolean
199+ , useApi :: Boolean
194− , useSso :: Boolean
200+ , useSso :: Boolean
195− , useKeyConnector :: Boolean
201+ , useKeyConnector :: Boolean
196− , useResetPassword :: Boolean
202+ , useResetPassword :: Boolean
197− , selfHost :: Boolean
203+ , selfHost :: Boolean
198− , usersGetPremium :: Boolean
204+ , usersGetPremium :: Boolean
199− , seats :: Int
205+ , seats :: Int
200− , maxCollections :: Int
206+ , maxCollections :: Int
201− , maxStorageGb :: JNullable Int
207+ , maxStorageGb :: JNullable Int
202− , key :: JNullable String
208+ , key :: JNullable String
203− , hasPublicAndPrivateKeys :: Boolean
209+ , hasPublicAndPrivateKeys :: Boolean
204− , status :: OrganizationUserStatusType
210+ , status :: OrganizationUserStatusType
205− , type :: OrganizationUserType
211+ , type :: OrganizationUserType
206− , enabled :: Boolean
212+ , enabled :: Boolean
207− , ssoBound :: Boolean
213+ , ssoBound :: Boolean
208− , identifier :: JNullable String
214+ , identifier :: JNullable String
209− , permissions :: PermissionsApi
215+ , permissions :: PermissionsApi
210− , resetPasswordEnrolled :: Boolean
216+ , resetPasswordEnrolled :: Boolean
211− , userId :: JNullable String
217+ , userId :: JNullable String
212− , providerId :: JNullable String
218+ , providerId :: JNullable String
213− , providerName :: JNullable String
219+ , providerName :: JNullable String
214− , familySponsorshipFriendlyName :: JNullable String
220+ , familySponsorshipFriendlyName :: JNullable String
215− , familySponsorshipAvailable :: Boolean
221+ , familySponsorshipAvailable :: Boolean
216− , planProductType :: ProductType
222+ , planProductType :: ProductType
217− , keyConnectorEnabled :: Boolean
223+ , keyConnectorEnabled :: Boolean
218− , keyConnectorUrl :: JNullable String
224+ , keyConnectorUrl :: JNullable String
219− , familySponsorshipLastSyncDate :: JNullable Timestamp
225+ , familySponsorshipLastSyncDate :: JNullable Timestamp
220− , familySponsorshipValidUntil :: JNullable Timestamp
226+ , familySponsorshipValidUntil :: JNullable Timestamp
221− , familySponsorshipToDelete :: JNullable Boolean
227+ , familySponsorshipToDelete :: JNullable Boolean
222− }
228+ }
223−
229+
224−type Urls
230+type Urls =
225− = { base :: JNullable String
231+ { base :: JNullable String
226− , webVault :: JNullable String
232+ , webVault :: JNullable String
227− , api :: JNullable String
233+ , api :: JNullable String
228− , identity :: JNullable String
234+ , identity :: JNullable String
229− , icons :: JNullable String
235+ , icons :: JNullable String
230− , notifications :: JNullable String
236+ , notifications :: JNullable String
231− , events :: JNullable String
237+ , events :: JNullable String
232− , keyConnector :: JNullable String
238+ , keyConnector :: JNullable String
233− }
239+ }
234−
240+
235−type PasswordTokenRequest
241+type PasswordTokenRequest =
236− = { email :: Email
242+ { email :: Email
237− , masterPasswordHash :: String
243+ , masterPasswordHash :: String
238− , captchaResponse :: String
244+ , captchaResponse :: String
239− , twoFactor :: TokenRequestTwoFactor
245+ , twoFactor :: TokenRequestTwoFactor
240− , device :: DeviceRequest
246+ , device :: DeviceRequest
241− }
247+ }
242−
248+
243−type DeviceRequest
249+type DeviceRequest =
244− = { type :: DeviceType
250+ { type :: DeviceType
245− , name :: String
251+ , name :: String
246− , identifier :: String
252+ , identifier :: String
247− , pushToken :: JNullable String
253+ , pushToken :: JNullable String
248− }
254+ }
249
255
250deviceTypeAndroid = 0 :: DeviceType
256deviceTypeAndroid = 0 :: DeviceType
251
257
@@ -289,14 +295,13 @@ deviceTypeVivaldiExtension = 19 :: DeviceType
289
295
290deviceTypeSafariExtension = 20 :: DeviceType
296deviceTypeSafariExtension = 20 :: DeviceType
291
297
292−type DeviceType
298+type DeviceType = Int
293− = Int
294
299
295−type TokenRequestTwoFactor
300+type TokenRequestTwoFactor =
296− = { provider :: TwoFactorProviderType
301+ { provider :: TwoFactorProviderType
297− , token :: String
302+ , token :: String
298− , remember :: Boolean
303+ , remember :: Boolean
299− }
304+ }
300
305
301twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
306twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
302
307
@@ -314,12 +319,11 @@ twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
314
319
315twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
320twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
316
321
317−type TwoFactorProviderType
322+type TwoFactorProviderType = Int
318− = Int
319
323
320−secondFactorTypeToBridge ::
324+secondFactorTypeToBridge
321− TwoFactorProviderType ->
325+ :: TwoFactorProviderType
322− Maybe Bridge.TwoFactorProviderType
326+ -> Maybe Bridge.TwoFactorProviderType
323secondFactorTypeToBridge x = case x of
327secondFactorTypeToBridge x = case x of
324 n
328 n
325 | n == twoFactorProviderTypeAuthenticator -> Just Bridge.Authenticator
329 | n == twoFactorProviderTypeAuthenticator -> Just Bridge.Authenticator
@@ -349,21 +353,17 @@ bridgeToSecondFactorType Bridge.Remember = twoFactorProviderTypeRemember
349bridgeToSecondFactorType Bridge.OrganizationDuo = twoFactorProviderTypeOrganizationDuo
353bridgeToSecondFactorType Bridge.OrganizationDuo = twoFactorProviderTypeOrganizationDuo
350bridgeToSecondFactorType Bridge.WebAuthn = twoFactorProviderTypeWebAuthn
354bridgeToSecondFactorType Bridge.WebAuthn = twoFactorProviderTypeWebAuthn
351
355
352−newtype Password
356+newtype Password = Password String
353− = Password String
354
357
355hashPurposeServerAuthorization = 1 :: HashPurpose
358hashPurposeServerAuthorization = 1 :: HashPurpose
356
359
357hashPurposeLocalAuthorization = 2 :: HashPurpose
360hashPurposeLocalAuthorization = 2 :: HashPurpose
358
361
359−type HashPurpose
362+type HashPurpose = Int
360− = Int
361
363
362−newtype StringHash
364+newtype StringHash = StringHash String
363− = StringHash String
364
365
365−newtype AccessToken
366+newtype AccessToken = AccessToken String
366− = AccessToken String
367
367
368derive newtype instance Show AccessToken
368derive newtype instance Show AccessToken
369derive newtype instance Eq AccessToken
369derive newtype instance Eq AccessToken
@@ -371,8 +371,7 @@ derive newtype instance Ord AccessToken
371derive newtype instance EncodeJson AccessToken
371derive newtype instance EncodeJson AccessToken
372derive newtype instance DecodeJson AccessToken
372derive newtype instance DecodeJson AccessToken
373
373
374−newtype RefreshToken
374+newtype RefreshToken = RefreshToken String
375− = RefreshToken String
376
375
377derive newtype instance Show RefreshToken
376derive newtype instance Show RefreshToken
378derive newtype instance Eq RefreshToken
377derive newtype instance Eq RefreshToken
@@ -384,27 +383,33 @@ type IdentityCaptchaResponse =
384 { siteKey :: String
383 { siteKey :: String
385 }
384 }
386
385
387−type IdentityTokenResponse
386+type IdentityTokenResponse =
388− = { accessToken :: AccessToken
387+ { accessToken :: AccessToken
389− , expiresIn :: Int
388+ , expiresIn :: Int
390− , refreshToken :: RefreshToken
389+ , refreshToken :: RefreshToken
391− , tokenType :: String
390+ , tokenType :: String
392− , resetMasterPassword :: Boolean
391+ , resetMasterPassword :: Boolean
393− , privateKey :: EncryptedString
392+ , privateKey :: EncryptedString
394− , key :: EncryptedString
393+ , key :: EncryptedString
395− , twoFactorToken :: JNullable String
394+ , twoFactorToken :: JNullable String
396− , kdf :: KDF
395+ , kdf :: KDF
397− , kdfIterations :: Int
396+ , kdfIterations :: Int
398− , forcePasswordReset :: JNullable Boolean
397+ , forcePasswordReset :: JNullable Boolean
399− , apiUseKeyConnector :: JNullable Boolean
398+ , apiUseKeyConnector :: JNullable Boolean
400− , keyConnectorUrl :: JNullable String
399+ , keyConnectorUrl :: JNullable String
401− }
400+ }
402−
401+
403−newtype TwoFactorProviderTypes = TwoFactorProviderTypes (Array TwoFactorProviderType)
402+newtype TwoFactorProviderTypes = TwoFactorProviderTypes (Array (Int |+| String))
404
403
405instance HasRuntimeType TwoFactorProviderTypes where
404instance HasRuntimeType TwoFactorProviderTypes where
406 hasRuntimeType Proxy = Foreign.isArray
405 hasRuntimeType Proxy = Foreign.isArray
407
406
407+fromTwoFactorProviderType :: TwoFactorProviderTypes -> Either String (Array Bridge.TwoFactorProviderType)
408+fromTwoFactorProviderType (TwoFactorProviderTypes xs) =
409+ for xs $ \rawProvider -> do
410+ providerInt <- Union.reduce (Either.Right /\ (\x -> Either.note x $ Int.fromString x)) rawProvider
411+ Either.note (show providerInt) $ secondFactorTypeToBridge providerInt
412+
408type IdentityTwoFactorResponse =
413type IdentityTwoFactorResponse =
409 { twoFactorProviders :: TwoFactorProviderTypes
414 { twoFactorProviders :: TwoFactorProviderTypes
410 -- it is a Map type and untaged unions gets confused
415 -- it is a Map type and untaged unions gets confused
@@ -412,59 +417,57 @@ type IdentityTwoFactorResponse =
412 , captchaToken :: String |+| Undefined
417 , captchaToken :: String |+| Undefined
413 }
418 }
414
419
415−newtype Email
420+newtype Email = Email String
416− = Email String
417−
418−type SyncResponse
419− = { profile :: ProfileResponse
420− , folders :: Array FolderResponse
421− , collections :: Array CollectionDetailsResponse
422− , ciphers :: Array CipherResponse
423− , domains :: JNullable DomainsResponse
424− , policies :: JNullable (Array PolicyResponse)
425− , sends :: Array SendResponse
426− }
427
421
428−type FolderResponse
422+type SyncResponse =
429− = { id :: String
423+ { profile :: ProfileResponse
430− , name :: JNullable String
424+ , folders :: Array FolderResponse
431− , revisionDate :: JNullable String
425+ , collections :: Array CollectionDetailsResponse
426+ , ciphers :: Array CipherResponse
427+ , domains :: JNullable DomainsResponse
428+ , policies :: JNullable (Array PolicyResponse)
429+ , sends :: Array SendResponse
432− }
430+ }
433
431
434−type CollectionDetailsResponse
432+type FolderResponse =
435− = { id :: String
433+ { id :: String
436− , organizationId :: JNullable String
437− , name :: JNullable String
434+ , name :: JNullable String
438− , externalId :: JNullable String
435+ , revisionDate :: JNullable String
439− , readOnly :: Boolean
440− }
436+ }
441
437
438+type CollectionDetailsResponse =
439+ { id :: String
440+ , organizationId :: JNullable String
441+ , name :: JNullable String
442+ , externalId :: JNullable String
443+ , readOnly :: Boolean
444+ }
442
445
443type CipherId = String
446type CipherId = String
444
447
445−type CipherResponse
448+type CipherResponse =
446− = { id :: CipherId
449+ { id :: CipherId
447− , organizationId :: JNullable String
450+ , organizationId :: JNullable String
448− , folderId :: JNullable String
451+ , folderId :: JNullable String
449− , type :: CipherType
452+ , type :: CipherType
450− , name :: EncryptedString
453+ , name :: EncryptedString
451− , notes :: JNullable EncryptedString
454+ , notes :: JNullable EncryptedString
452− , fields :: JNullable (Array FieldApi)
455+ , fields :: JNullable (Array FieldApi)
453− , login :: JNullable LoginApi
456+ , login :: JNullable LoginApi
454− , card :: JNullable CardApi
457+ , card :: JNullable CardApi
455− , identity :: JNullable IdentityApi
458+ , identity :: JNullable IdentityApi
456− , secureNote :: JNullable SecureNoteApi
459+ , secureNote :: JNullable SecureNoteApi
457− , favorite :: Boolean
460+ , favorite :: Boolean
458− , edit :: JNullable Boolean
461+ , edit :: JNullable Boolean
459− , viewPassword :: JNullable Boolean
462+ , viewPassword :: JNullable Boolean
460− , organizationUseTotp :: JNullable Boolean
463+ , organizationUseTotp :: JNullable Boolean
461− , revisionDate :: JNullable Timestamp
464+ , revisionDate :: JNullable Timestamp
462− , attachments :: JNullable (Array AttachmentResponse)
465+ , attachments :: JNullable (Array AttachmentResponse)
463− , passwordHistory :: JNullable (Array PasswordHistoryResponse)
466+ , passwordHistory :: JNullable (Array PasswordHistoryResponse)
464− , collectionIds :: JNullable (Array String)
467+ , collectionIds :: JNullable (Array String)
465− , deletedDate :: JNullable String
468+ , deletedDate :: JNullable String
466− , reprompt :: CipherRepromptType
469+ , reprompt :: CipherRepromptType
467− }
470+ }
468
471
469cipherTypeLogin = 1 :: CipherType
472cipherTypeLogin = 1 :: CipherType
470
473
@@ -474,15 +477,14 @@ cipherTypeCard = 3 :: CipherType
474
477
475cipherTypeIdentity = 4 :: CipherType
478cipherTypeIdentity = 4 :: CipherType
476
479
477−type CipherType
480+type CipherType = Int
478− = Int
479
481
480−type FieldApi
482+type FieldApi =
481− = { name :: JNullable String
483+ { name :: JNullable String
482− , value :: JNullable String
484+ , value :: JNullable String
483− , type :: FieldType
485+ , type :: FieldType
484− , linkedId :: JNullable LinkedIdType
486+ , linkedId :: JNullable LinkedIdType
485− }
487+ }
486
488
487fieldTypeText = 0 :: FieldType
489fieldTypeText = 0 :: FieldType
488
490
@@ -492,8 +494,7 @@ fieldTypeBoolean = 2 :: FieldType
492
494
493fieldTypeLinked = 3 :: FieldType
495fieldTypeLinked = 3 :: FieldType
494
496
495−type FieldType
497+type FieldType = Int
496− = Int
497
498
498linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
499linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
499
500
@@ -549,23 +550,22 @@ linkedIdTypeIdentityLinkedIdLastName = 417 :: LinkedIdType
549
550
550linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
551linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
551
552
552−type LinkedIdType
553+type LinkedIdType = Int
553− = Int
554
554
555−type LoginApi
555+type LoginApi =
556− = { uris :: JOpt (Array LoginUriApi)
556+ { uris :: JOpt (Array LoginUriApi)
557− , username :: JNullable EncryptedString
557+ , username :: JNullable EncryptedString
558− , password :: JNullable EncryptedString
558+ , password :: JNullable EncryptedString
559− , passwordRevisionDate :: JNullable String
559+ , passwordRevisionDate :: JNullable String
560− , totp :: JNullable EncryptedString
560+ , totp :: JNullable EncryptedString
561− -- This thing is undefined in some cases for some reson. Just ignoring it
561+ -- This thing is undefined in some cases for some reson. Just ignoring it
562− , autofillOnPageLoad :: JOpt (JNullable Boolean)
562+ , autofillOnPageLoad :: JOpt (JNullable Boolean)
563− }
563+ }
564
564
565−type LoginUriApi
565+type LoginUriApi =
566− = { uri :: EncryptedString
566+ { uri :: JNullable EncryptedString
567− , match :: JNullable UriMatchType
567+ , match :: JNullable UriMatchType
568− }
568+ }
569
569
570uriMatchTypeDomain = 0 :: UriMatchType
570uriMatchTypeDomain = 0 :: UriMatchType
571
571
@@ -579,87 +579,85 @@ uriMatchTypeRegularExpression = 4 :: UriMatchType
579
579
580uriMatchTypeNever = 5 :: UriMatchType
580uriMatchTypeNever = 5 :: UriMatchType
581
581
582−type UriMatchType
582+type UriMatchType = Int
583− = Int
583+
584−
585−type CardApi
584+type CardApi =
586− = { cardholderName :: JNullable EncryptedString
585+ { cardholderName :: JNullable EncryptedString
587− , brand :: JNullable EncryptedString
586+ , brand :: JNullable EncryptedString
588− , number :: JNullable EncryptedString
587+ , number :: JNullable EncryptedString
589− , expMonth :: JNullable EncryptedString
588+ , expMonth :: JNullable EncryptedString
590− , expYear :: JNullable EncryptedString
589+ , expYear :: JNullable EncryptedString
591− , code :: JNullable EncryptedString
590+ , code :: JNullable EncryptedString
592− }
591+ }
593−
592+
594−type IdentityApi
593+type IdentityApi =
595− = { title :: JNullable EncryptedString
594+ { title :: JNullable EncryptedString
596− , firstName :: JNullable EncryptedString
595+ , firstName :: JNullable EncryptedString
597− , middleName :: JNullable EncryptedString
596+ , middleName :: JNullable EncryptedString
598− , lastName :: JNullable EncryptedString
597+ , lastName :: JNullable EncryptedString
599− , address1 :: JNullable EncryptedString
598+ , address1 :: JNullable EncryptedString
600− , address2 :: JNullable EncryptedString
599+ , address2 :: JNullable EncryptedString
601− , address3 :: JNullable EncryptedString
600+ , address3 :: JNullable EncryptedString
602− , city :: JNullable EncryptedString
601+ , city :: JNullable EncryptedString
603− , state :: JNullable EncryptedString
602+ , state :: JNullable EncryptedString
604− , postalCode :: JNullable EncryptedString
603+ , postalCode :: JNullable EncryptedString
605− , country :: JNullable EncryptedString
604+ , country :: JNullable EncryptedString
606− , company :: JNullable EncryptedString
605+ , company :: JNullable EncryptedString
607− , email :: JNullable EncryptedString
606+ , email :: JNullable EncryptedString
608− , phone :: JNullable EncryptedString
607+ , phone :: JNullable EncryptedString
609− , ssn :: JNullable EncryptedString
608+ , ssn :: JNullable EncryptedString
610− , username :: JNullable EncryptedString
609+ , username :: JNullable EncryptedString
611− , passportNumber :: JNullable EncryptedString
610+ , passportNumber :: JNullable EncryptedString
612− , licenseNumber :: JNullable EncryptedString
611+ , licenseNumber :: JNullable EncryptedString
613− }
612+ }
614
613
615secureNoteTypeGeneric = 0 :: SecureNoteType
614secureNoteTypeGeneric = 0 :: SecureNoteType
616
615
617−type SecureNoteType
616+type SecureNoteType = Int
618− = Int
619
617
620−type SecureNoteApi = {
618+type SecureNoteApi =
619+ {
621 -- type :: SecureNoteType
620 -- type :: SecureNoteType
622−}
621+ }
623−
622+
624−type AttachmentResponse
623+type AttachmentResponse =
625− = { id :: String
624+ { id :: String
626− , url :: JNullable String
625+ , url :: JNullable String
627− , fileName :: JNullable String
626+ , fileName :: JNullable String
628− , key :: JNullable String
627+ , key :: JNullable String
629− , size :: JNullable String
628+ , size :: JNullable String
630− , sizeName :: JNullable String
629+ , sizeName :: JNullable String
631− }
630+ }
632−
631+
633−type PasswordHistoryResponse
632+type PasswordHistoryResponse =
634− = { password :: String
633+ { password :: String
635− , lastUsedDate :: JNullable String
634+ , lastUsedDate :: JNullable String
636− }
635+ }
637
636
638cipherRepromptTypeNone = 0 :: CipherRepromptType
637cipherRepromptTypeNone = 0 :: CipherRepromptType
639
638
640cipherRepromptTypePassword = 1 :: CipherRepromptType
639cipherRepromptTypePassword = 1 :: CipherRepromptType
641
640
642−type CipherRepromptType
641+type CipherRepromptType = Int
643− = Int
644
642
645−type DomainsResponse
643+type DomainsResponse =
646− = { equivalentDomains :: Array (Array String)
644+ { equivalentDomains :: Array (Array String)
647− , globalEquivalentDomains :: Array GlobalDomainResponse
645+ , globalEquivalentDomains :: Array GlobalDomainResponse
648− }
646+ }
649
647
650−type GlobalDomainResponse
648+type GlobalDomainResponse =
651− = { type :: Int
649+ { type :: Int
652− , domains :: Array String
650+ , domains :: Array String
653− , excluded :: Boolean
651+ , excluded :: Boolean
654− }
652+ }
655
653
656−type PolicyResponse
654+type PolicyResponse =
657− = { id :: String
655+ { id :: String
658− , organizationId :: JNullable String
656+ , organizationId :: JNullable String
659− , type :: PolicyType
657+ , type :: PolicyType
660− , data :: ShowableJson
658+ , data :: ShowableJson
661− , enabled :: Boolean
659+ , enabled :: Boolean
662− }
660+ }
663
661
664policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
662policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
665
663
@@ -683,46 +681,44 @@ policyTypeMaximumVaultTimeout = 9 :: PolicyType -- Sets the maximum allowed vaul
683
681
684policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
682policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
685
683
686−type PolicyType
684+type PolicyType = Int
687− = Int
685+
688−
689−type SendResponse
686+type SendResponse =
690− = { id :: String
687+ { id :: String
691− , accessId :: JNullable String
688+ , accessId :: JNullable String
692− , type :: SendType
689+ , type :: SendType
693− , name :: JNullable String
690+ , name :: JNullable String
694− , notes :: JNullable String
691+ , notes :: JNullable String
695− , file :: JNullable SendFileApi
692+ , file :: JNullable SendFileApi
696− , text :: JNullable SendTextApi
693+ , text :: JNullable SendTextApi
697− , key :: JNullable String
694+ , key :: JNullable String
698− , maxAccessCount :: JNullable Int
695+ , maxAccessCount :: JNullable Int
699− , accessCount :: JNullable Int
696+ , accessCount :: JNullable Int
700− , revisionDate :: JNullable String
697+ , revisionDate :: JNullable String
701− , expirationDate :: JNullable String
698+ , expirationDate :: JNullable String
702− , deletionDate :: JNullable String
699+ , deletionDate :: JNullable String
703− , password :: JNullable String
700+ , password :: JNullable String
704− , disable :: Boolean
701+ , disable :: Boolean
705− , hideEmail :: Boolean
702+ , hideEmail :: Boolean
706− }
703+ }
707
704
708sendTypeText = 0 :: SendType
705sendTypeText = 0 :: SendType
709
706
710sendTypeFile = 1 :: SendType
707sendTypeFile = 1 :: SendType
711
708
712−type SendType
709+type SendType = Int
713− = Int
714
710
715−type SendFileApi
711+type SendFileApi =
716− = { id :: String
712+ { id :: String
717− , fileName :: JNullable String
713+ , fileName :: JNullable String
718− , size :: JNullable String
714+ , size :: JNullable String
719− , sizeName :: JNullable String
715+ , sizeName :: JNullable String
720− }
716+ }
721
717
722−type SendTextApi
718+type SendTextApi =
723− = { text :: JNullable String
719+ { text :: JNullable String
724− , hidden :: Boolean
720+ , hidden :: Boolean
725− }
721+ }
726
722
727type TwoFactorEmailRequest =
723type TwoFactorEmailRequest =
728 { email :: Email
724 { email :: Email
backend/Data/EncString.js+1−1
@@ -1,4 +1,4 @@
1−import { EncString } from "../../deps/bw/libs/shared/dist/src/models/domain/encString"
1+import { EncString } from "../../bw/models/domain/enc-string"
2
2
3export function fromString(str) {
3export function fromString(str) {
4 const x = new EncString(str)
4 const x = new EncString(str)
backend/Data/SymmetricCryptoKey.js+1−2
@@ -1,6 +1,5 @@
1−import { SymmetricCryptoKey } from '../../deps/bw/libs/shared/dist/src/models/domain/symmetricCryptoKey.js';
1+import { SymmetricCryptoKey } from '../../bw/models/domain/symmetric-crypto-key.js'
2
2
3export function fromArrayBuffer(arr) {
3export function fromArrayBuffer(arr) {
4− console.log("fromArrayBuffer " + arr)
5 return new SymmetricCryptoKey(arr)
4 return new SymmetricCryptoKey(arr)
6}
5}
backend/Main.purs+145−138
@@ -3,19 +3,20 @@ module Main
3 ) where
3 ) where
4
4
5import Prelude
5import Prelude
6+
6import BW (ApiService, CryptoFunctions, Services, CryptoService)
7import BW (ApiService, CryptoFunctions, Services, CryptoService)
7import BW as WB
8import BW as WB
8−import BW.Logic (bwPasswordStringHash, decodeCipher, decrypt, encodeCipher, hashPassword, liftPromise)
9+import BW.Logic (bwPasswordStringHash, decodeCipher, decrypt, encodeCipher, hashPassword, liftPromise, supportedCipher)
9import BW.Logic as Logic
10import BW.Logic as Logic
10−import BW.Types (CipherResponse, Email(..), Password(..), TwoFactorProviderTypes(..), Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote, secondFactorTypeToBridge)
11+import BW.Types (CipherResponse, Email(..), Password(..), Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote, fromTwoFactorProviderType)
11import Bridge as Bridge
12import Bridge as Bridge
12−import Control.Monad.Error.Class (catchError, throwError)
13+import Control.Monad.Error.Class (catchError)
13−import Control.Monad.Error.Class as Error
14import Data.Argonaut (class DecodeJson)
14import Data.Argonaut (class DecodeJson)
15import Data.Array as Array
15import Data.Array as Array
16import Data.Clipboard as Clipboard
16import Data.Clipboard as Clipboard
17import Data.DateTime (DateTime)
17import Data.DateTime (DateTime)
18import Data.Either (Either(..))
18import Data.Either (Either(..))
19+import Data.Foldable (for_)
19import Data.HCaptcha (bindHCaptchToken)
20import Data.HCaptcha (bindHCaptchToken)
20import Data.JNullable (jnull, nullify)
21import Data.JNullable (jnull, nullify)
21import Data.JNullable as JNullable
22import Data.JNullable as JNullable
@@ -29,7 +30,7 @@ import Effect (Effect)
29import Effect.Aff (Aff, runAff_, try)
30import Effect.Aff (Aff, runAff_, try)
30import Effect.Class (liftEffect)
31import Effect.Class (liftEffect)
31import Effect.Class.Console (log)
32import Effect.Class.Console (log)
32−import Effect.Exception (error)
33+import Effect.Exception (throw)
33import Effect.Exception as Exc
34import Effect.Exception as Exc
34import Effect.Ref as Ref
35import Effect.Ref as Ref
35import Elm as Elm
36import Elm as Elm
@@ -61,19 +62,19 @@ main = do
61 let
62 let
62 runAff act = runElmAff app $ run $ act
63 runAff act = runElmAff app $ run $ act
63
64
64− run ::
65+ run
65− forall a.
66+ :: forall a
66− Run
67+ . Run
67− ( app :: Reader Elm
68+ ( app :: Reader Elm
68− , aff :: Aff
69+ , aff :: Aff
69− , effect :: Effect
70+ , effect :: Effect
70− , crypto :: Reader CryptoService
71+ , crypto :: Reader CryptoService
71− , storage :: Reader Storage
72+ , storage :: Reader Storage
72− , services :: Reader Services
73+ , services :: Reader Services
73− , cryptoFunctions :: Reader CryptoFunctions
74+ , cryptoFunctions :: Reader CryptoFunctions
74− )
75+ )
75− a ->
76+ a
76− Run (EFFECT + AFF + ()) a
77+ -> Run (EFFECT + AFF + ()) a
77 run act =
78 run act =
78 runReaderAt (Proxy :: _ "app") app
79 runReaderAt (Proxy :: _ "app") app
79 $ runReaderAt (Proxy :: _ "crypto") services.crypto
80 $ runReaderAt (Proxy :: _ "crypto") services.crypto
@@ -82,19 +83,19 @@ main = do
82 $ runReaderAt (Proxy :: _ "cryptoFunctions") services.cryptoFunctions
83 $ runReaderAt (Proxy :: _ "cryptoFunctions") services.cryptoFunctions
83 $ act
84 $ act
84
85
85− runWithDecryptionKey ::
86+ runWithDecryptionKey
86− Run
87+ :: Run
87− ( app :: Reader Elm
88+ ( app :: Reader Elm
88− , aff :: Aff
89+ , aff :: Aff
89− , effect :: Effect
90+ , effect :: Effect
90− , key :: Reader SymmetricCryptoKey
91+ , key :: Reader SymmetricCryptoKey
91− , crypto :: Reader CryptoService
92+ , crypto :: Reader CryptoService
92− , storage :: Reader Storage
93+ , storage :: Reader Storage
93− , services :: Reader Services
94+ , services :: Reader Services
94− , cryptoFunctions :: Reader CryptoFunctions
95+ , cryptoFunctions :: Reader CryptoFunctions
95− )
96+ )
96− Unit ->
97+ Unit
97− Effect Unit
98+ -> Effect Unit
98 runWithDecryptionKey act = do
99 runWithDecryptionKey act = do
99 maybeKey <- Ref.read masterKeyRef
100 maybeKey <- Ref.read masterKeyRef
100 runAff
101 runAff
@@ -131,7 +132,10 @@ main = do
131 }
132 }
132 pure $ Right resp
133 pure $ Right resp
133 )
134 )
135+ ( \err -> do
136+ log $ show err
134− (const $ pure $ Left unit)
137+ pure $ Left unit
138+ )
135 case map (toEither1 >>> map toEither1) $ loginResponse of
139 case map (toEither1 >>> map toEither1) $ loginResponse of
136 Left _ -> send Bridge.WrongPassword
140 Left _ -> send Bridge.WrongPassword
137 Right (Left { siteKey }) -> send $ Bridge.NeedsCaptcha siteKey
141 Right (Left { siteKey }) -> send $ Bridge.NeedsCaptcha siteKey
@@ -149,20 +153,15 @@ main = do
149 Storage.store storage SyncKey sync
153 Storage.store storage SyncKey sync
150 Storage.store storage TokenKey token
154 Storage.store storage TokenKey token
151 send Bridge.LoginSuccessful
155 send Bridge.LoginSuccessful
152− Right (Right (Left { twoFactorProviders: TwoFactorProviderTypes rawProviders, captchaToken })) -> do
156+ Right (Right (Left { twoFactorProviders, captchaToken })) -> do
153 case toEither1 captchaToken of
157 case toEither1 captchaToken of
154 Left captchaTokenString ->
158 Left captchaTokenString ->
155 liftEffect
159 liftEffect
156 $ Ref.write (Just captchaTokenString) hCaptchaTokenRef
160 $ Ref.write (Just captchaTokenString) hCaptchaTokenRef
157 Right _ -> pure unit
161 Right _ -> pure unit
158− providers <-
162+ providers <- case fromTwoFactorProviderType twoFactorProviders of
159− liftEffect
160− $ traverse
161− ( \x ->
162− secondFactorTypeToBridge x
163− # Error.liftMaybe (error $ "Unknown second factor provider: " <> show x)
163+ Left x -> liftEffect $ throw $ "Unknown second factor provider: " <> x
164− )
164+ Right x -> pure x
165− rawProviders
166 send $ Bridge.NeedsSecondFactor $ Bridge.Sub_NeedsSecondFactor_List providers
165 send $ Bridge.NeedsSecondFactor $ Bridge.Sub_NeedsSecondFactor_List providers
167 Bridge.NeedCiphersList ->
166 Bridge.NeedCiphersList ->
168 runWithDecryptionKey do
167 runWithDecryptionKey do
@@ -187,9 +186,9 @@ main = do
187 Bridge.Init -> do
186 Bridge.Init -> do
188 Storage.get storage TokenKey
187 Storage.get storage TokenKey
189 >>= \x ->
188 >>= \x ->
190− Elm.send app case x of
189+ Elm.send app case x of
191− Just _ -> Bridge.LoginSuccessful
190+ Just _ -> Bridge.LoginSuccessful
192− Nothing -> Bridge.NeedsLogin
191+ Nothing -> Bridge.NeedsLogin
193 Bridge.RequestCipher id ->
192 Bridge.RequestCipher id ->
194 runWithDecryptionKey do
193 runWithDecryptionKey do
195 sync <- getOrReset SyncKey
194 sync <- getOrReset SyncKey
@@ -197,8 +196,7 @@ main = do
197 Nothing -> pure unit
196 Nothing -> pure unit
198 Just cipherResponse -> do
197 Just cipherResponse -> do
199 cipher <- decodeCipher cipherResponse
198 cipher <- decodeCipher cipherResponse
200− send $ Bridge.LoadCipher cipher
199+ for_ cipher $ send <<< Bridge.LoadCipher
201− pure unit
202 pure unit
200 pure unit
203 Bridge.NeedEmail ->
201 Bridge.NeedEmail ->
204 runWithDecryptionKey do
202 runWithDecryptionKey do
@@ -214,7 +212,7 @@ main = do
214 api <- getAuthedApi
212 api <- getAuthedApi
215 cipher <- encodeCipher fullCipher
213 cipher <- encodeCipher fullCipher
216 newCipher <- liftPromise (api.putCipher cipher) >>= decodeCipher
214 newCipher <- liftPromise (api.putCipher cipher) >>= decodeCipher
217− send $ Bridge.CipherChanged newCipher
215+ for_ newCipher $ send <<< Bridge.CipherChanged
218 performSync
216 performSync
219 pure unit
217 pure unit
220 Bridge.GeneratePassword cfg ->
218 Bridge.GeneratePassword cfg ->
@@ -227,7 +225,7 @@ main = do
227 api <- getAuthedApi
225 api <- getAuthedApi
228 cipher <- encodeCipher fullCipher
226 cipher <- encodeCipher fullCipher
229 newCipher <- liftPromise (api.postCipherCreate cipher) >>= decodeCipher
227 newCipher <- liftPromise (api.postCipherCreate cipher) >>= decodeCipher
230− send $ Bridge.CipherChanged newCipher
228+ for_ newCipher $ send <<< Bridge.CipherChanged
231 performSync
229 performSync
232 pure unit
230 pure unit
233 Bridge.DeleteCipher c@(Bridge.FullCipher { id }) ->
231 Bridge.DeleteCipher c@(Bridge.FullCipher { id }) ->
@@ -245,14 +243,20 @@ main = do
245 interval = totpService.getTimeInterval totp
243 interval = totpService.getTimeInterval totp
246 send $ Bridge.Totp
244 send $ Bridge.Totp
247 $ Bridge.Sub_Totp
245 $ Bridge.Sub_Totp
246+ { interval
247+ , code
248− { interval, code, source: totp
248+ , source: totp
249 }
249 }
250 pure unit
250 pure unit
251 Bridge.ChooseSecondFactor
251 Bridge.ChooseSecondFactor
252 ( Bridge.Cmd_ChooseSecondFactor
252 ( Bridge.Cmd_ChooseSecondFactor
253+ { email: email'
254+ , factor
255+ , server
256+ , password
253− { email: email', factor, server, password, requestFromServer
257+ , requestFromServer
254− }
258+ }
255− ) ->
259+ ) ->
256 when requestFromServer
260 when requestFromServer
257 let
261 let
258 email = Email email'
262 email = Email email'
@@ -269,17 +273,18 @@ main = do
269 pure unit
273 pure unit
270 _ -> pure unit
274 _ -> pure unit
271
275
272−processCipher ::
276+processCipher
273− forall r.
277+ :: forall r
274− CipherResponse ->
278+ . CipherResponse
275− Run
279+ -> Run
276− ( crypto :: Reader CryptoService
280+ ( crypto :: Reader CryptoService
277− , key :: Reader SymmetricCryptoKey
281+ , key :: Reader SymmetricCryptoKey
278− , aff :: Aff
282+ , aff :: Aff
279− , effect :: Effect
283+ , effect :: Effect
280− | r
284+ | r
281− )
285+ )
282− { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime }
286+ (Maybe { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime })
287+processCipher cipher | not (supportedCipher cipher) = pure Nothing
283processCipher cipher = do
288processCipher cipher = do
284 name <- decrypt cipher.name
289 name <- decrypt cipher.name
285 cipherType <- case cipher.type of
290 cipherType <- case cipher.type of
@@ -291,10 +296,12 @@ processCipher cipher = do
291 | n == cipherTypeCard -> pure Bridge.CardType
296 | n == cipherTypeCard -> pure Bridge.CardType
292 n
297 n
293 | n == cipherTypeIdentity -> pure Bridge.IdentityType
298 | n == cipherTypeIdentity -> pure Bridge.IdentityType
294− n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
299+ n -> liftEffect $ throw $ "Unsupported cipher type: " <> show n
295 date <- liftEffect $ maybe (pure bottom) (Timestamp.toDateTime) $ JNullable.toMaybe cipher.revisionDate
300 date <- liftEffect $ maybe (pure bottom) (Timestamp.toDateTime) $ JNullable.toMaybe cipher.revisionDate
296 pure
301 pure
302+ $ Just
303+ $
297− $ { cipher:
304+ { cipher:
298 Bridge.Sub_LoadCiphers
305 Bridge.Sub_LoadCiphers
299 { name: name
306 { name: name
300 , date: maybe "" Timestamp.toLocalDateTimeString $ JNullable.toMaybe cipher.revisionDate
307 , date: maybe "" Timestamp.toLocalDateTimeString $ JNullable.toMaybe cipher.revisionDate
@@ -328,18 +335,18 @@ baseUrl server =
328 , keyConnector: jnull
335 , keyConnector: jnull
329 }
336 }
330
337
331−getOrReset ::
338+getOrReset
332− forall k t r.
339+ :: forall k t r
333− StorageKey k t =>
340+ . StorageKey k t
334− DecodeJson t =>
341+ => DecodeJson t
335− k ->
342+ => k
336− Run
343+ -> Run
337− ( storage :: Reader Storage
344+ ( storage :: Reader Storage
338− , app :: Reader Elm
345+ , app :: Reader Elm
339− , effect :: Effect
346+ , effect :: Effect
340− | r
347+ | r
341− )
348+ )
342− t
349+ t
343getOrReset key = do
350getOrReset key = do
344 storage <- askAt (Proxy :: _ "storage")
351 storage <- askAt (Proxy :: _ "storage")
345 app <- askAt (Proxy :: _ "app")
352 app <- askAt (Proxy :: _ "app")
@@ -350,69 +357,69 @@ getOrReset key = do
350 Exc.throw "Encountered an internal error. Resetting the app."
357 Exc.throw "Encountered an internal error. Resetting the app."
351 liftEffect $ try (Storage.get storage key)
358 liftEffect $ try (Storage.get storage key)
352 >>= \x -> case x of
359 >>= \x -> case x of
353− Left err -> do
360+ Left err -> do
354− log $ show err
361+ log $ show err
355− handleError
362+ handleError
356− Right Nothing -> handleError
363+ Right Nothing -> handleError
357− Right (Just y) -> pure y
364+ Right (Just y) -> pure y
358
365
359−getAuthedApi ::
366+getAuthedApi
360− forall r.
367+ :: forall r
361− Run
368+ . Run
362− ( storage :: Reader Storage
369+ ( storage :: Reader Storage
363− , app :: Reader Elm
370+ , app :: Reader Elm
364− , aff :: Aff
371+ , aff :: Aff
365− , effect :: Effect
372+ , effect :: Effect
366− , services :: Reader Services
373+ , services :: Reader Services
367− | r
374+ | r
368− )
375+ )
369− ApiService
376+ ApiService
370getAuthedApi = do
377getAuthedApi = do
371 services <- askAt (Proxy :: _ "services")
378 services <- askAt (Proxy :: _ "services")
372 token <- getOrReset TokenKey
379 token <- getOrReset TokenKey
373 url <- getOrReset UrlsKey
380 url <- getOrReset UrlsKey
374 liftPromise $ services.getApi (baseUrl url) (nullify token)
381 liftPromise $ services.getApi (baseUrl url) (nullify token)
375
382
376−requestMasterPassword ::
383+requestMasterPassword
377− forall r.
384+ :: forall r
378− Run
385+ . Run
379− ( storage :: Reader Storage
386+ ( storage :: Reader Storage
380− , app :: Reader Elm
387+ , app :: Reader Elm
381− , effect :: Effect
388+ , effect :: Effect
382− | r
389+ | r
383− )
390+ )
384− Unit
391+ Unit
385requestMasterPassword = do
392requestMasterPassword = do
386 url <- getOrReset UrlsKey
393 url <- getOrReset UrlsKey
387 sync <- getOrReset SyncKey
394 sync <- getOrReset SyncKey
388 send $ Bridge.NeedsMasterPassword $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
395 send $ Bridge.NeedsMasterPassword $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
389
396
390−send ::
397+send
391− forall r.
398+ :: forall r
392− Bridge.Sub ->
399+ . Bridge.Sub
393− Run
400+ -> Run
394− ( app :: Reader Elm
401+ ( app :: Reader Elm
395− , effect :: Effect
402+ , effect :: Effect
396− | r
403+ | r
397− )
404+ )
398− Unit
405+ Unit
399send sub = do
406send sub = do
400 app <- askAt (Proxy :: _ "app")
407 app <- askAt (Proxy :: _ "app")
401 liftEffect $ Elm.send app sub
408 liftEffect $ Elm.send app sub
402
409
403−performSync ::
410+performSync
404− forall r.
411+ :: forall r
405− Run
412+ . Run
406− ( storage :: Reader Storage
413+ ( storage :: Reader Storage
407− , app :: Reader Elm
414+ , app :: Reader Elm
408− , aff :: Aff
415+ , aff :: Aff
409− , effect :: Effect
416+ , effect :: Effect
410− , services :: Reader Services
417+ , services :: Reader Services
411− , crypto :: Reader CryptoService
418+ , crypto :: Reader CryptoService
412− , key :: Reader SymmetricCryptoKey
419+ , key :: Reader SymmetricCryptoKey
413− | r
420+ | r
414− )
421+ )
415− Unit
422+ Unit
416performSync = do
423performSync = do
417 api <- getAuthedApi
424 api <- getAuthedApi
418 storage <- askAt (Proxy :: _ "storage")
425 storage <- askAt (Proxy :: _ "storage")
@@ -420,22 +427,22 @@ performSync = do
420 liftEffect $ Storage.store storage SyncKey newSync
427 liftEffect $ Storage.store storage SyncKey newSync
421 sendCiphers
428 sendCiphers
422
429
423−sendCiphers ::
430+sendCiphers
424− forall r.
431+ :: forall r
425− Run
432+ . Run
426− ( storage :: Reader Storage
433+ ( storage :: Reader Storage
427− , app :: Reader Elm
434+ , app :: Reader Elm
428− , aff :: Aff
435+ , aff :: Aff
429− , effect :: Effect
436+ , effect :: Effect
430− , services :: Reader Services
437+ , services :: Reader Services
431− , crypto :: Reader CryptoService
438+ , crypto :: Reader CryptoService
432− , key :: Reader SymmetricCryptoKey
439+ , key :: Reader SymmetricCryptoKey
433− | r
440+ | r
434− )
441+ )
435− Unit
442+ Unit
436sendCiphers = do
443sendCiphers = do
437 sync <- getOrReset SyncKey
444 sync <- getOrReset SyncKey
438− ciphers <- traverse processCipher sync.ciphers
445+ ciphers <- Array.catMaybes <$> traverse processCipher sync.ciphers
439 let
446 let
440 sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
447 sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
441 send $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
448 send $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
backend/Storage.purs+7−1
@@ -2,7 +2,7 @@ module Storage where
2
2
3import Prelude
3import Prelude
4
4
5−import BW (Hash(..))
5+import BW (Hash)
6import BW.Types (IdentityTokenResponse, SyncResponse, PreloginResponse)
6import BW.Types (IdentityTokenResponse, SyncResponse, PreloginResponse)
7import Localstorage (class StorageKey)
7import Localstorage (class StorageKey)
8
8
@@ -35,3 +35,9 @@ data MasterPasswordHashKey
35
35
36instance StorageKey MasterPasswordHashKey Hash where
36instance StorageKey MasterPasswordHashKey Hash where
37 storageKey MasterPasswordHashKey = "master-password-hash"
37 storageKey MasterPasswordHashKey = "master-password-hash"
38+
39+data DeviceIDKey
40+ = DeviceIDKey
41+
42+instance StorageKey DeviceIDKey String where
43+ storageKey DeviceIDKey = "device-uuid"
build.sh+2−14
@@ -1,17 +1,5 @@
1−#!/bin/bash
1+#!/bin/sh
2
2
3set -e
3set -e
4
4
5−TEMP=$(mktemp -d)
5+cp -r "$SRC_DIR/dist"/* "$INSTALL_DIR"
6−
7−cp -r "$SRC_DIR"/{appname.apparmor,appname.desktop,manifest.json,logo.png,index.html,style.css} "$INSTALL_DIR"
8−mkdir -p "$INSTALL_DIR"/vanilla
9−cp -r "$SRC_DIR"/vanilla/dist "$INSTALL_DIR"/vanilla
10−
11−elm make "$SRC_DIR/frontend/Main.elm" --optimize --output="$TEMP/elm.js"
12−
13−terser "$TEMP/elm.js" --ecma 2018 --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | terser --ecma 2018 --mangle --compress --output "$INSTALL_DIR/elm.js"
14−
15−(cd "$SRC_DIR" && spago bundle-app --to "$TEMP/backend.js")
16−
17−terser "$TEMP/backend.js" --ecma 2018 --mangle --compress 'drop_console=true' --comments=false --toplevel --output "$INSTALL_DIR/backend.js"
clickable.yaml+2−10
@@ -1,16 +1,8 @@
1clickable_minimum_required: 7.1.2
1clickable_minimum_required: 7.1.2
2+framework: ubuntu-sdk-20.04
2builder: custom
3builder: custom
3build: $SRC_DIR/build.sh
4build: $SRC_DIR/build.sh
4kill: webapp-container*
5kill: webapp-container*
5−image_setup:
6+docker_image: clickable/amd64-20.04-amd64
6− env: {}
7− # DEB_URL: http://archive.ubuntu.com/ubuntu
8− # HOST_ARCH: amd64
9−
10− # PATH: /opt/someprogram/bin:$PATH
11− run:
12−
13− - (cd /tmp && curl -L -o elm.gz https://github.com/elm/compiler/releases/download/0.19.1/binary-for-linux-64-bit.gz && gunzip elm.gz && chmod +x elm && mv elm /usr/local/bin/)
14− - npm install -g spago esbuild purescript terser --unsafe-perm=true
15
7
16restrict_arch: all
8restrict_arch: all
deps/bwdeleted−1

Submodule changed.

dev/build-backend.sh+7−1
@@ -1,3 +1,9 @@
1#!/bin/sh
1#!/bin/sh
2
2
3−spago bundle-app --to backend.js
3+set -e
4+
5+spago build
6+
7+export out="$(pwd)/backend.js"
8+
9+$(nix build .#backendBuildScript --print-out-paths) "$(pwd)/output"
dev/build.shnew file+10
@@ -0,0 +1,10 @@
1+#!/bin/sh
2+
3+
4+rm -r dist
5+
6+mkdir -p dist
7+
8+cp -r "$(nix build ".#warden-click-src" --print-out-paths)"/* dist
9+
10+chmod -R 777 dist
dev/elm-live.sh+5−1
@@ -1,3 +1,7 @@
1#!/bin/sh
1#!/bin/sh
2
2
3+set -e
4+
5+nix build .#vanilla -o vanilla
6+
3−nix run github:NixOS/nixpkgs?rev=d9a1414346059619d9e13ab93e749bbb82e5252a#elmPackages.elm-live -- $1 --start-page=index.html --proxy-prefix=/api --proxy-host=http://localhost:6009 --pushstate -- --debug --output=elm.js
7+nix run github:NixOS/nixpkgs?rev=d9a1414346059619d9e13ab93e749bbb82e5252a#elmPackages.elm-live -- $1 --start-page=index.html --proxy-prefix=/api --proxy-host=http://localhost:6009 -- --debug --output=elm.js
dev/elm2nix.shnew file+8
@@ -0,0 +1,8 @@
1+#!/bin/sh
2+
3+set -e
4+
5+cd "$(dirname "$0")/.."
6+
7+nix run github:NixOS/nixpkgs#elm2nix -- snapshot
8+nix run github:NixOS/nixpkgs#elm2nix -- convert > elm-srcs.nix
dev/spago2nix.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+nix run "github:justinwoo/spago2nix#spago2nix" -- generate
dev/start-chrome.sh+3−1
@@ -1,6 +1,8 @@
1#!/bin/sh
1#!/bin/sh
2
2
3+if test -x "$(which chromium)"; then
4+ chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
3−if test -f /Applications/Chromium.app/Contents/MacOS/Chromium; then
5+elif test -x /Applications/Chromium.app/Contents/MacOS/Chromium; then
4 /Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
6 /Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
5else
7else
6 flatpak run com.github.Eloston.UngoogledChromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
8 flatpak run com.github.Eloston.UngoogledChromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
elm-srcs.nixnew file+62
@@ -0,0 +1,62 @@
1+{
2+
3+ "FabienHenon/elm-infinite-scroll" = {
4+ sha256 = "0vhh7j3lna05l5hwjazag52lbkjv11s80jj62m9wb53qxkarw948";
5+ version = "3.0.3";
6+ };
7+
8+ "bartavelle/json-helpers" = {
9+ sha256 = "0k96qra2nq1j4j4ahfl98dkpkc6f2831mq5d5xxg27mp31qwq5nn";
10+ version = "2.0.2";
11+ };
12+
13+ "elm/browser" = {
14+ sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
15+ version = "1.0.2";
16+ };
17+
18+ "elm/core" = {
19+ sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
20+ version = "1.0.5";
21+ };
22+
23+ "elm/html" = {
24+ sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
25+ version = "1.0.0";
26+ };
27+
28+ "elm/json" = {
29+ sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
30+ version = "1.1.3";
31+ };
32+
33+ "elm/time" = {
34+ sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
35+ version = "1.0.0";
36+ };
37+
38+ "mdgriffith/elm-ui" = {
39+ sha256 = "0ffcqv4a4ad400hwp824m3qq4jy82cqp5ghmhp1m0q7n004z6kgv";
40+ version = "1.1.8";
41+ };
42+
43+ "mgold/elm-nonempty-list" = {
44+ sha256 = "02d3xgasgy5knh02rgl403z9hwlbyrsnp9wavc2lhkxxa2misrgl";
45+ version = "4.2.0";
46+ };
47+
48+ "elm/random" = {
49+ sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
50+ version = "1.0.0";
51+ };
52+
53+ "elm/url" = {
54+ sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
55+ version = "1.0.0";
56+ };
57+
58+ "elm/virtual-dom" = {
59+ sha256 = "1yvb8px2z62xd578ag2q0r5hd1vkz9y7dfkx05355iiy1d7jwq4v";
60+ version = "1.0.3";
61+ };
62+}
flake.locknew file+177
@@ -0,0 +1,177 @@
1+{
2+ "nodes": {
3+ "bw": {
4+ "flake": false,
5+ "locked": {
6+ "lastModified": 1675446731,
7+ "narHash": "sha256-IbFjYNYZep6W9c+dztJ9CXrwQ0cep3EKb6qnEvv4pj4=",
8+ "owner": "bitwarden",
9+ "repo": "clients",
10+ "rev": "ff143760d4f527f2349333ee4b3004eeda68fac5",
11+ "type": "github"
12+ },
13+ "original": {
14+ "owner": "bitwarden",
15+ "repo": "clients",
16+ "rev": "ff143760d4f527f2349333ee4b3004eeda68fac5",
17+ "type": "github"
18+ }
19+ },
20+ "easy-ps": {
21+ "flake": false,
22+ "locked": {
23+ "lastModified": 1671011575,
24+ "narHash": "sha256-tESal32bcqqdZO+aKnBzc1GoL2mtnaDtj2y7ociCRGA=",
25+ "owner": "justinwoo",
26+ "repo": "easy-purescript-nix",
27+ "rev": "11d3bd58ce6e32703bf69cec04dc7c38eabe14ba",
28+ "type": "github"
29+ },
30+ "original": {
31+ "owner": "justinwoo",
32+ "repo": "easy-purescript-nix",
33+ "type": "github"
34+ }
35+ },
36+ "easy-purescript-nix": {
37+ "flake": false,
38+ "locked": {
39+ "lastModified": 1670198178,
40+ "narHash": "sha256-5KkyNpPakv4xIP2ba0S5GX+dcmd3AcO9kPhwa482BbA=",
41+ "owner": "justinwoo",
42+ "repo": "easy-purescript-nix",
43+ "rev": "7a4cb3cd6ca53566ea1675692eab0aa13907ff09",
44+ "type": "github"
45+ },
46+ "original": {
47+ "owner": "justinwoo",
48+ "repo": "easy-purescript-nix",
49+ "type": "github"
50+ }
51+ },
52+ "flake-utils": {
53+ "locked": {
54+ "lastModified": 1676283394,
55+ "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
56+ "owner": "numtide",
57+ "repo": "flake-utils",
58+ "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
59+ "type": "github"
60+ },
61+ "original": {
62+ "owner": "numtide",
63+ "repo": "flake-utils",
64+ "type": "github"
65+ }
66+ },
67+ "flake-utils_2": {
68+ "locked": {
69+ "lastModified": 1667395993,
70+ "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
71+ "owner": "numtide",
72+ "repo": "flake-utils",
73+ "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
74+ "type": "github"
75+ },
76+ "original": {
77+ "owner": "numtide",
78+ "repo": "flake-utils",
79+ "type": "github"
80+ }
81+ },
82+ "nix-filter": {
83+ "locked": {
84+ "lastModified": 1676294984,
85+ "narHash": "sha256-hdLUa/3RH1VJ+gMUysQE0JGM4F2Q/tIIFbtoxAOurJQ=",
86+ "owner": "numtide",
87+ "repo": "nix-filter",
88+ "rev": "fc282c5478e4141842f9644c239a41cfe9586732",
89+ "type": "github"
90+ },
91+ "original": {
92+ "owner": "numtide",
93+ "repo": "nix-filter",
94+ "type": "github"
95+ }
96+ },
97+ "nixpkgs": {
98+ "locked": {
99+ "lastModified": 1677080879,
100+ "narHash": "sha256-0SjW4/d3Rkw6C7hHZ5lxT4r6Pw9vzQb6Il6zYWwe2Bo=",
101+ "owner": "NixOS",
102+ "repo": "nixpkgs",
103+ "rev": "f5dad40450d272a1ea2413f4a67ac08760649e89",
104+ "type": "github"
105+ },
106+ "original": {
107+ "id": "nixpkgs",
108+ "type": "indirect"
109+ }
110+ },
111+ "nixpkgs_2": {
112+ "locked": {
113+ "lastModified": 1670507980,
114+ "narHash": "sha256-riNZa0xzM1it3pzxciwALeMs+0CsBMWIW2FqulzK8vM=",
115+ "owner": "NixOS",
116+ "repo": "nixpkgs",
117+ "rev": "2787fc7d1e51404678614bf0fe92fc296746eec0",
118+ "type": "github"
119+ },
120+ "original": {
121+ "owner": "NixOS",
122+ "ref": "nixos-unstable",
123+ "repo": "nixpkgs",
124+ "type": "github"
125+ }
126+ },
127+ "root": {
128+ "inputs": {
129+ "bw": "bw",
130+ "easy-ps": "easy-ps",
131+ "flake-utils": "flake-utils",
132+ "nix-filter": "nix-filter",
133+ "nixpkgs": "nixpkgs",
134+ "spago2nix": "spago2nix",
135+ "vanilla": "vanilla"
136+ }
137+ },
138+ "spago2nix": {
139+ "inputs": {
140+ "easy-purescript-nix": "easy-purescript-nix",
141+ "flake-utils": "flake-utils_2",
142+ "nixpkgs": "nixpkgs_2"
143+ },
144+ "locked": {
145+ "lastModified": 1671225584,
146+ "narHash": "sha256-h9r67pmvDuA3TV9299L4CN60XSm8RRtX1EwUoKu9Pyw=",
147+ "owner": "justinwoo",
148+ "repo": "spago2nix",
149+ "rev": "1b8ec352bc7eac077b934d6b9f6efa0129926e59",
150+ "type": "github"
151+ },
152+ "original": {
153+ "owner": "justinwoo",
154+ "repo": "spago2nix",
155+ "type": "github"
156+ }
157+ },
158+ "vanilla": {
159+ "flake": false,
160+ "locked": {
161+ "lastModified": 1663430347,
162+ "narHash": "sha256-J+KyCcCMdC8SYINKAtUsNfM0/lnbwOnsY8TKOP+yY0s=",
163+ "owner": "ilyakooo0",
164+ "repo": "vanilla-compiled",
165+ "rev": "7a0ee46ce9b958b51c57b37c46e86a0a1868bfa9",
166+ "type": "github"
167+ },
168+ "original": {
169+ "owner": "ilyakooo0",
170+ "repo": "vanilla-compiled",
171+ "type": "github"
172+ }
173+ }
174+ },
175+ "root": "root",
176+ "version": 7
177+}
flake.nixnew file+133
@@ -0,0 +1,133 @@
1+{
2+ inputs = {
3+ flake-utils.url = "github:numtide/flake-utils";
4+ bw = {
5+ url =
6+ # Afer this commit they started requiring path at the top level and I can't deal with this right now
7+ "github:bitwarden/clients?rev=ff143760d4f527f2349333ee4b3004eeda68fac5";
8+ flake = false;
9+ };
10+ spago2nix.url = "github:justinwoo/spago2nix";
11+ easy-ps = {
12+ url = "github:justinwoo/easy-purescript-nix";
13+ flake = false;
14+ };
15+ vanilla = {
16+ url = "github:ilyakooo0/vanilla-compiled";
17+ flake = false;
18+ };
19+ nix-filter.url = "github:numtide/nix-filter";
20+ };
21+
22+ outputs = inputs@{ self, nixpkgs, flake-utils, spago2nix, ... }:
23+ flake-utils.lib.eachDefaultSystem (system:
24+ let
25+ pkgs = import nixpkgs { inherit system; };
26+ spagoPkgs = pkgs.callPackage ./spago-packages.nix { };
27+ easy-ps = pkgs.callPackage inputs.easy-ps { };
28+ nix-filter = import inputs.nix-filter;
29+
30+ bw = pkgs.buildNpmPackage {
31+ pname = "bw";
32+ version = "latest";
33+ npmDepsHash = "sha256-jQvL7/SzXqVk03ncU6Mxq8lqFFy+lIrF2QheheXrQ0w=";
34+ makeCacheWritable = true;
35+ src = inputs.bw;
36+ nativeBuildInputs = [ pkgs.python3 ];
37+ buildPhase = ''
38+ (cd libs/common/ && rm -r spec && find . -name "*.spec.ts" -exec rm {} \; && npm exec -c "tsc -m es2020 -t es2018") '';
39+ installPhase = ''
40+ mkdir -p $out
41+ cp -r libs/shared/dist/* $out
42+ cp -r node_modules $out/node_modules
43+ cp package.json $out/package.json
44+ '';
45+ ELECTRON_SKIP_BINARY_DOWNLOAD = 1;
46+ };
47+
48+ backendBuildScript = pkgs.writeScript "build-backend.sh" ''
49+ workdir=$(mktemp -d)
50+ (
51+ cd $workdir
52+ cp -r $1 ./backend
53+ cp -r ${bw} ./bw
54+ cp ${./backend-main.js} backend.js
55+ ${pkgs.esbuild}/bin/esbuild backend.js --bundle --define:ENVIRONMENT_IS_WEB=true --define:ENVIRONMENT_IS_WORKER=false --define:ENVIRONMENT_IS_NODE=false --define:ENVIRONMENT_IS_SHELL=false --external:fs --external:path --loader:.wasm=base64 --outfile=$out
56+ )
57+
58+ '';
59+ backend-js = pkgs.stdenv.mkDerivation {
60+ name = "backend-js";
61+ buildInputs =
62+ [ spagoPkgs.installSpagoStyle spagoPkgs.buildSpagoStyle ];
63+ nativeBuildInputs = [ easy-ps.purs-0_15_7 easy-ps.spago ];
64+ src = nix-filter {
65+ root = ./.;
66+ include = [
67+ ./spago.dhall
68+ ./packages.dhall
69+ (nix-filter.inDirectory ./backend)
70+ ];
71+ };
72+ unpackPhase = ''
73+ cp $src/spago.dhall .
74+ cp $src/packages.dhall .
75+ cp -r $src/backend .
76+ install-spago-style
77+ '';
78+ buildPhase = ''
79+ build-spago-style "./backend/**/*.purs"
80+ '';
81+ installPhase = ''
82+ mkdir $out
83+ cp -r output/* $out/
84+ '';
85+ };
86+
87+ backend =
88+ pkgs.runCommand "backend.js" { buildInputs = [ pkgs.esbuild ]; } ''
89+ ${backendBuildScript} ${backend-js}
90+ '';
91+
92+ frontend = pkgs.callPackage ./frontend.nix { inherit nix-filter; };
93+
94+ warden-src = pkgs.runCommand "warden-src" { } ''
95+ mkdir -p $out
96+
97+ cp ${backend} $out/backend.js
98+ cp -r ${frontend}/Main.min.js $out/elm.js
99+ cp ${./index.html} $out/index.html
100+ cp ${./style.css} $out/style.css
101+ cp -r ${inputs.vanilla} $out/vanilla
102+ '';
103+
104+ warden-click-src = pkgs.runCommand "warden-click-src" { } ''
105+ mkdir -p $out
106+
107+ cp ${./warden.apparmor} $out/warden.apparmor
108+ cp ${./warden.desktop} $out/warden.desktop
109+ cp ${./manifest.json} $out/manifest.json
110+ cp ${./logo.png} $out/logo.png
111+ cp -r ${warden-src}/* $out/
112+ '';
113+
114+ warden = pkgs.writeScript "warden" ''
115+ ${pkgs.electron}/bin/electron ${warden-src}/index.html
116+ '';
117+
118+ in {
119+ packages = {
120+ inherit bw frontend warden-src backendBuildScript warden-click-src;
121+ vanilla = inputs.vanilla.outPath;
122+ };
123+ apps = {
124+ inherit (spago2nix.apps.${system}) spago2nix;
125+ warden = {
126+ type = "app";
127+ program = warden.outPath;
128+ };
129+ };
130+
131+ devShells.default = import ./shell.nix { inherit pkgs; };
132+ });
133+}
frontend.nixnew file+49
@@ -0,0 +1,49 @@
1+{ pkgs, nix-filter }:
2+with pkgs;
3+
4+let
5+ mkDerivation = { srcs ? ./elm-srcs.nix, src, name, srcdir ? "./frontend"
6+ , targets ? [ ], registryDat ? ./registry.dat, outputJavaScript ? true }:
7+ stdenv.mkDerivation {
8+ inherit name src;
9+
10+ buildInputs = [ elmPackages.elm ]
11+ ++ lib.optional outputJavaScript nodePackages.uglify-js;
12+
13+ buildPhase = pkgs.elmPackages.fetchElmDeps {
14+ elmPackages = import srcs;
15+ elmVersion = "0.19.1";
16+ inherit registryDat;
17+ };
18+
19+ installPhase = let
20+ elmfile = module:
21+ "${srcdir}/${builtins.replaceStrings [ "." ] [ "/" ] module}.elm";
22+ extension = if outputJavaScript then "js" else "html";
23+ in ''
24+ mkdir -p $out/share/doc
25+ ${lib.concatStrings (map (module: ''
26+ echo "compiling ${elmfile module}"
27+ elm make ${
28+ elmfile module
29+ } --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
30+ ${lib.optionalString outputJavaScript ''
31+ echo "minifying ${elmfile module}"
32+ uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
33+ | uglifyjs --mangle --output $out/${module}.min.${extension}
34+ ''}
35+ '') targets)}
36+ '';
37+ };
38+in mkDerivation {
39+ name = "frontend";
40+ srcs = ./elm-srcs.nix;
41+ src = nix-filter {
42+ root = ./.;
43+ include = [ (nix-filter.inDirectory ./frontend) ./elm.json ];
44+ };
45+ targets = [ "Main" ];
46+ srcdir = "./frontend";
47+ outputJavaScript = true;
48+}
49+
frontend/Main.elm+3−6
@@ -432,9 +432,6 @@ update msg model =
432 _ ->
432 _ ->
433 False
433 False
434 )
434 )
435−
436− pageModel =
437− Ciphers.init ciphersCallbacks ciphers
438 in
435 in
439 ( if ciphersShown then
436 ( if ciphersShown then
440 { model
437 { model
@@ -443,8 +440,8 @@ update msg model =
443 |> Nonempty.map
440 |> Nonempty.map
444 (\x ->
441 (\x ->
445 case x of
442 case x of
446− CiphersModel _ ->
443+ CiphersModel pageModel ->
447− CiphersModel pageModel
444+ CiphersModel (Ciphers.updateCiphers ciphers pageModel)
448
445
449 other ->
446 other ->
450 other
447 other
@@ -452,7 +449,7 @@ update msg model =
452 }
449 }
453
450
454 else
451 else
455− appendPageStack <| CiphersModel pageModel
452+ appendPageStack <| CiphersModel (Ciphers.init ciphersCallbacks ciphers)
456 , Cmd.none
453 , Cmd.none
457 )
454 )
458
455
frontend/Pages/Ciphers.elm+17−1
@@ -1,4 +1,15 @@
1−module Pages.Ciphers exposing (Callbacks, Model, Msg, event, init, menuConfig, title, update, view)
1+module Pages.Ciphers exposing
2+ ( Callbacks
3+ , Model
4+ , Msg
5+ , event
6+ , init
7+ , menuConfig
8+ , title
9+ , update
10+ , updateCiphers
11+ , view
12+ )
2
13
3import Bridge
14import Bridge
4import GlobalEvents
15import GlobalEvents
@@ -26,6 +37,11 @@ type alias Model emsg =
26 }
37 }
27
38
28
39
40+updateCiphers : Bridge.Sub_LoadCiphers_List -> Model emsg -> Model emsg
41+updateCiphers ciphers model =
42+ { model | ciphers = ciphers }
43+
44+
29type CiphersFilter
45type CiphersFilter
30 = AllCiphers
46 = AllCiphers
31 | SpecificCiphers Bridge.CipherType
47 | SpecificCiphers Bridge.CipherType
index.html+1−1
@@ -7,7 +7,7 @@
7 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
8 <link rel="stylesheet" href="vanilla/dist/style.css">
8 <link rel="stylesheet" href="vanilla/dist/style.css">
9 <link rel="stylesheet" href="style.css">
9 <link rel="stylesheet" href="style.css">
10− <title>Test.</title>
10+ <title>Warden</title>
11 <script src="elm.js"></script>
11 <script src="elm.js"></script>
12</head>
12</head>
13
13
manifest.json+5−5
@@ -4,12 +4,12 @@
4 "architecture": "@CLICK_ARCH@",
4 "architecture": "@CLICK_ARCH@",
5 "title": "Warden",
5 "title": "Warden",
6 "hooks": {
6 "hooks": {
7− "appname": {
7+ "warden": {
8− "apparmor": "appname.apparmor",
8+ "apparmor": "warden.apparmor",
9− "desktop": "appname.desktop"
9+ "desktop": "warden.desktop"
10 }
10 }
11 },
11 },
12− "version": "1.1.0",
12+ "version": "1.2.4",
13− "maintainer": "Ilia Kostiuchenko <email@iko.soy>",
13+ "maintainer": "Ilia Kostiuchenko <mail@iko.soy>",
14 "framework": "@CLICK_FRAMEWORK@"
14 "framework": "@CLICK_FRAMEWORK@"
15}
15}
package-lock.jsonnew file+959
@@ -0,0 +1,959 @@
1+{
2+ "name": "warden",
3+ "lockfileVersion": 3,
4+ "requires": true,
5+ "packages": {
6+ "": {
7+ "devDependencies": {
8+ "cssnano": "^5.1.15"
9+ }
10+ },
11+ "node_modules/@trysound/sax": {
12+ "version": "0.2.0",
13+ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
14+ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
15+ "dev": true,
16+ "engines": {
17+ "node": ">=10.13.0"
18+ }
19+ },
20+ "node_modules/boolbase": {
21+ "version": "1.0.0",
22+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
23+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
24+ "dev": true
25+ },
26+ "node_modules/browserslist": {
27+ "version": "4.21.5",
28+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
29+ "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
30+ "dev": true,
31+ "funding": [
32+ {
33+ "type": "opencollective",
34+ "url": "https://opencollective.com/browserslist"
35+ },
36+ {
37+ "type": "tidelift",
38+ "url": "https://tidelift.com/funding/github/npm/browserslist"
39+ }
40+ ],
41+ "dependencies": {
42+ "caniuse-lite": "^1.0.30001449",
43+ "electron-to-chromium": "^1.4.284",
44+ "node-releases": "^2.0.8",
45+ "update-browserslist-db": "^1.0.10"
46+ },
47+ "bin": {
48+ "browserslist": "cli.js"
49+ },
50+ "engines": {
51+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
52+ }
53+ },
54+ "node_modules/caniuse-api": {
55+ "version": "3.0.0",
56+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
57+ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
58+ "dev": true,
59+ "dependencies": {
60+ "browserslist": "^4.0.0",
61+ "caniuse-lite": "^1.0.0",
62+ "lodash.memoize": "^4.1.2",
63+ "lodash.uniq": "^4.5.0"
64+ }
65+ },
66+ "node_modules/caniuse-lite": {
67+ "version": "1.0.30001457",
68+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz",
69+ "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==",
70+ "dev": true,
71+ "funding": [
72+ {
73+ "type": "opencollective",
74+ "url": "https://opencollective.com/browserslist"
75+ },
76+ {
77+ "type": "tidelift",
78+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
79+ }
80+ ]
81+ },
82+ "node_modules/colord": {
83+ "version": "2.9.3",
84+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
85+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
86+ "dev": true
87+ },
88+ "node_modules/commander": {
89+ "version": "7.2.0",
90+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
91+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
92+ "dev": true,
93+ "engines": {
94+ "node": ">= 10"
95+ }
96+ },
97+ "node_modules/css-declaration-sorter": {
98+ "version": "6.3.1",
99+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz",
100+ "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==",
101+ "dev": true,
102+ "engines": {
103+ "node": "^10 || ^12 || >=14"
104+ },
105+ "peerDependencies": {
106+ "postcss": "^8.0.9"
107+ }
108+ },
109+ "node_modules/css-select": {
110+ "version": "4.3.0",
111+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
112+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
113+ "dev": true,
114+ "dependencies": {
115+ "boolbase": "^1.0.0",
116+ "css-what": "^6.0.1",
117+ "domhandler": "^4.3.1",
118+ "domutils": "^2.8.0",
119+ "nth-check": "^2.0.1"
120+ },
121+ "funding": {
122+ "url": "https://github.com/sponsors/fb55"
123+ }
124+ },
125+ "node_modules/css-tree": {
126+ "version": "1.1.3",
127+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
128+ "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
129+ "dev": true,
130+ "dependencies": {
131+ "mdn-data": "2.0.14",
132+ "source-map": "^0.6.1"
133+ },
134+ "engines": {
135+ "node": ">=8.0.0"
136+ }
137+ },
138+ "node_modules/css-what": {
139+ "version": "6.1.0",
140+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
141+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
142+ "dev": true,
143+ "engines": {
144+ "node": ">= 6"
145+ },
146+ "funding": {
147+ "url": "https://github.com/sponsors/fb55"
148+ }
149+ },
150+ "node_modules/cssesc": {
151+ "version": "3.0.0",
152+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
153+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
154+ "dev": true,
155+ "bin": {
156+ "cssesc": "bin/cssesc"
157+ },
158+ "engines": {
159+ "node": ">=4"
160+ }
161+ },
162+ "node_modules/cssnano": {
163+ "version": "5.1.15",
164+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz",
165+ "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
166+ "dev": true,
167+ "dependencies": {
168+ "cssnano-preset-default": "^5.2.14",
169+ "lilconfig": "^2.0.3",
170+ "yaml": "^1.10.2"
171+ },
172+ "engines": {
173+ "node": "^10 || ^12 || >=14.0"
174+ },
175+ "funding": {
176+ "type": "opencollective",
177+ "url": "https://opencollective.com/cssnano"
178+ },
179+ "peerDependencies": {
180+ "postcss": "^8.2.15"
181+ }
182+ },
183+ "node_modules/cssnano-preset-default": {
184+ "version": "5.2.14",
185+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
186+ "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
187+ "dev": true,
188+ "dependencies": {
189+ "css-declaration-sorter": "^6.3.1",
190+ "cssnano-utils": "^3.1.0",
191+ "postcss-calc": "^8.2.3",
192+ "postcss-colormin": "^5.3.1",
193+ "postcss-convert-values": "^5.1.3",
194+ "postcss-discard-comments": "^5.1.2",
195+ "postcss-discard-duplicates": "^5.1.0",
196+ "postcss-discard-empty": "^5.1.1",
197+ "postcss-discard-overridden": "^5.1.0",
198+ "postcss-merge-longhand": "^5.1.7",
199+ "postcss-merge-rules": "^5.1.4",
200+ "postcss-minify-font-values": "^5.1.0",
201+ "postcss-minify-gradients": "^5.1.1",
202+ "postcss-minify-params": "^5.1.4",
203+ "postcss-minify-selectors": "^5.2.1",
204+ "postcss-normalize-charset": "^5.1.0",
205+ "postcss-normalize-display-values": "^5.1.0",
206+ "postcss-normalize-positions": "^5.1.1",
207+ "postcss-normalize-repeat-style": "^5.1.1",
208+ "postcss-normalize-string": "^5.1.0",
209+ "postcss-normalize-timing-functions": "^5.1.0",
210+ "postcss-normalize-unicode": "^5.1.1",
211+ "postcss-normalize-url": "^5.1.0",
212+ "postcss-normalize-whitespace": "^5.1.1",
213+ "postcss-ordered-values": "^5.1.3",
214+ "postcss-reduce-initial": "^5.1.2",
215+ "postcss-reduce-transforms": "^5.1.0",
216+ "postcss-svgo": "^5.1.0",
217+ "postcss-unique-selectors": "^5.1.1"
218+ },
219+ "engines": {
220+ "node": "^10 || ^12 || >=14.0"
221+ },
222+ "peerDependencies": {
223+ "postcss": "^8.2.15"
224+ }
225+ },
226+ "node_modules/cssnano-utils": {
227+ "version": "3.1.0",
228+ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
229+ "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
230+ "dev": true,
231+ "engines": {
232+ "node": "^10 || ^12 || >=14.0"
233+ },
234+ "peerDependencies": {
235+ "postcss": "^8.2.15"
236+ }
237+ },
238+ "node_modules/csso": {
239+ "version": "4.2.0",
240+ "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
241+ "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
242+ "dev": true,
243+ "dependencies": {
244+ "css-tree": "^1.1.2"
245+ },
246+ "engines": {
247+ "node": ">=8.0.0"
248+ }
249+ },
250+ "node_modules/dom-serializer": {
251+ "version": "1.4.1",
252+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
253+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
254+ "dev": true,
255+ "dependencies": {
256+ "domelementtype": "^2.0.1",
257+ "domhandler": "^4.2.0",
258+ "entities": "^2.0.0"
259+ },
260+ "funding": {
261+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
262+ }
263+ },
264+ "node_modules/domelementtype": {
265+ "version": "2.3.0",
266+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
267+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
268+ "dev": true,
269+ "funding": [
270+ {
271+ "type": "github",
272+ "url": "https://github.com/sponsors/fb55"
273+ }
274+ ]
275+ },
276+ "node_modules/domhandler": {
277+ "version": "4.3.1",
278+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
279+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
280+ "dev": true,
281+ "dependencies": {
282+ "domelementtype": "^2.2.0"
283+ },
284+ "engines": {
285+ "node": ">= 4"
286+ },
287+ "funding": {
288+ "url": "https://github.com/fb55/domhandler?sponsor=1"
289+ }
290+ },
291+ "node_modules/domutils": {
292+ "version": "2.8.0",
293+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
294+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
295+ "dev": true,
296+ "dependencies": {
297+ "dom-serializer": "^1.0.1",
298+ "domelementtype": "^2.2.0",
299+ "domhandler": "^4.2.0"
300+ },
301+ "funding": {
302+ "url": "https://github.com/fb55/domutils?sponsor=1"
303+ }
304+ },
305+ "node_modules/electron-to-chromium": {
306+ "version": "1.4.302",
307+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.302.tgz",
308+ "integrity": "sha512-Uk7C+7aPBryUR1Fwvk9VmipBcN9fVsqBO57jV2ZjTm+IZ6BMNqu7EDVEg2HxCNufk6QcWlFsBkhQyQroB2VWKw==",
309+ "dev": true
310+ },
311+ "node_modules/entities": {
312+ "version": "2.2.0",
313+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
314+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
315+ "dev": true,
316+ "funding": {
317+ "url": "https://github.com/fb55/entities?sponsor=1"
318+ }
319+ },
320+ "node_modules/escalade": {
321+ "version": "3.1.1",
322+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
323+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
324+ "dev": true,
325+ "engines": {
326+ "node": ">=6"
327+ }
328+ },
329+ "node_modules/lilconfig": {
330+ "version": "2.0.6",
331+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
332+ "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
333+ "dev": true,
334+ "engines": {
335+ "node": ">=10"
336+ }
337+ },
338+ "node_modules/lodash.memoize": {
339+ "version": "4.1.2",
340+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
341+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
342+ "dev": true
343+ },
344+ "node_modules/lodash.uniq": {
345+ "version": "4.5.0",
346+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
347+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
348+ "dev": true
349+ },
350+ "node_modules/mdn-data": {
351+ "version": "2.0.14",
352+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
353+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
354+ "dev": true
355+ },
356+ "node_modules/nanoid": {
357+ "version": "3.3.4",
358+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
359+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
360+ "dev": true,
361+ "peer": true,
362+ "bin": {
363+ "nanoid": "bin/nanoid.cjs"
364+ },
365+ "engines": {
366+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
367+ }
368+ },
369+ "node_modules/node-releases": {
370+ "version": "2.0.10",
371+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
372+ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
373+ "dev": true
374+ },
375+ "node_modules/normalize-url": {
376+ "version": "6.1.0",
377+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
378+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
379+ "dev": true,
380+ "engines": {
381+ "node": ">=10"
382+ },
383+ "funding": {
384+ "url": "https://github.com/sponsors/sindresorhus"
385+ }
386+ },
387+ "node_modules/nth-check": {
388+ "version": "2.1.1",
389+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
390+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
391+ "dev": true,
392+ "dependencies": {
393+ "boolbase": "^1.0.0"
394+ },
395+ "funding": {
396+ "url": "https://github.com/fb55/nth-check?sponsor=1"
397+ }
398+ },
399+ "node_modules/picocolors": {
400+ "version": "1.0.0",
401+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
402+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
403+ "dev": true
404+ },
405+ "node_modules/postcss": {
406+ "version": "8.4.21",
407+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
408+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
409+ "dev": true,
410+ "funding": [
411+ {
412+ "type": "opencollective",
413+ "url": "https://opencollective.com/postcss/"
414+ },
415+ {
416+ "type": "tidelift",
417+ "url": "https://tidelift.com/funding/github/npm/postcss"
418+ }
419+ ],
420+ "peer": true,
421+ "dependencies": {
422+ "nanoid": "^3.3.4",
423+ "picocolors": "^1.0.0",
424+ "source-map-js": "^1.0.2"
425+ },
426+ "engines": {
427+ "node": "^10 || ^12 || >=14"
428+ }
429+ },
430+ "node_modules/postcss-calc": {
431+ "version": "8.2.4",
432+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
433+ "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
434+ "dev": true,
435+ "dependencies": {
436+ "postcss-selector-parser": "^6.0.9",
437+ "postcss-value-parser": "^4.2.0"
438+ },
439+ "peerDependencies": {
440+ "postcss": "^8.2.2"
441+ }
442+ },
443+ "node_modules/postcss-colormin": {
444+ "version": "5.3.1",
445+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
446+ "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
447+ "dev": true,
448+ "dependencies": {
449+ "browserslist": "^4.21.4",
450+ "caniuse-api": "^3.0.0",
451+ "colord": "^2.9.1",
452+ "postcss-value-parser": "^4.2.0"
453+ },
454+ "engines": {
455+ "node": "^10 || ^12 || >=14.0"
456+ },
457+ "peerDependencies": {
458+ "postcss": "^8.2.15"
459+ }
460+ },
461+ "node_modules/postcss-convert-values": {
462+ "version": "5.1.3",
463+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
464+ "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
465+ "dev": true,
466+ "dependencies": {
467+ "browserslist": "^4.21.4",
468+ "postcss-value-parser": "^4.2.0"
469+ },
470+ "engines": {
471+ "node": "^10 || ^12 || >=14.0"
472+ },
473+ "peerDependencies": {
474+ "postcss": "^8.2.15"
475+ }
476+ },
477+ "node_modules/postcss-discard-comments": {
478+ "version": "5.1.2",
479+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
480+ "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
481+ "dev": true,
482+ "engines": {
483+ "node": "^10 || ^12 || >=14.0"
484+ },
485+ "peerDependencies": {
486+ "postcss": "^8.2.15"
487+ }
488+ },
489+ "node_modules/postcss-discard-duplicates": {
490+ "version": "5.1.0",
491+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
492+ "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
493+ "dev": true,
494+ "engines": {
495+ "node": "^10 || ^12 || >=14.0"
496+ },
497+ "peerDependencies": {
498+ "postcss": "^8.2.15"
499+ }
500+ },
501+ "node_modules/postcss-discard-empty": {
502+ "version": "5.1.1",
503+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
504+ "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
505+ "dev": true,
506+ "engines": {
507+ "node": "^10 || ^12 || >=14.0"
508+ },
509+ "peerDependencies": {
510+ "postcss": "^8.2.15"
511+ }
512+ },
513+ "node_modules/postcss-discard-overridden": {
514+ "version": "5.1.0",
515+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
516+ "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
517+ "dev": true,
518+ "engines": {
519+ "node": "^10 || ^12 || >=14.0"
520+ },
521+ "peerDependencies": {
522+ "postcss": "^8.2.15"
523+ }
524+ },
525+ "node_modules/postcss-merge-longhand": {
526+ "version": "5.1.7",
527+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
528+ "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
529+ "dev": true,
530+ "dependencies": {
531+ "postcss-value-parser": "^4.2.0",
532+ "stylehacks": "^5.1.1"
533+ },
534+ "engines": {
535+ "node": "^10 || ^12 || >=14.0"
536+ },
537+ "peerDependencies": {
538+ "postcss": "^8.2.15"
539+ }
540+ },
541+ "node_modules/postcss-merge-rules": {
542+ "version": "5.1.4",
543+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
544+ "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
545+ "dev": true,
546+ "dependencies": {
547+ "browserslist": "^4.21.4",
548+ "caniuse-api": "^3.0.0",
549+ "cssnano-utils": "^3.1.0",
550+ "postcss-selector-parser": "^6.0.5"
551+ },
552+ "engines": {
553+ "node": "^10 || ^12 || >=14.0"
554+ },
555+ "peerDependencies": {
556+ "postcss": "^8.2.15"
557+ }
558+ },
559+ "node_modules/postcss-minify-font-values": {
560+ "version": "5.1.0",
561+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
562+ "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
563+ "dev": true,
564+ "dependencies": {
565+ "postcss-value-parser": "^4.2.0"
566+ },
567+ "engines": {
568+ "node": "^10 || ^12 || >=14.0"
569+ },
570+ "peerDependencies": {
571+ "postcss": "^8.2.15"
572+ }
573+ },
574+ "node_modules/postcss-minify-gradients": {
575+ "version": "5.1.1",
576+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
577+ "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
578+ "dev": true,
579+ "dependencies": {
580+ "colord": "^2.9.1",
581+ "cssnano-utils": "^3.1.0",
582+ "postcss-value-parser": "^4.2.0"
583+ },
584+ "engines": {
585+ "node": "^10 || ^12 || >=14.0"
586+ },
587+ "peerDependencies": {
588+ "postcss": "^8.2.15"
589+ }
590+ },
591+ "node_modules/postcss-minify-params": {
592+ "version": "5.1.4",
593+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
594+ "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
595+ "dev": true,
596+ "dependencies": {
597+ "browserslist": "^4.21.4",
598+ "cssnano-utils": "^3.1.0",
599+ "postcss-value-parser": "^4.2.0"
600+ },
601+ "engines": {
602+ "node": "^10 || ^12 || >=14.0"
603+ },
604+ "peerDependencies": {
605+ "postcss": "^8.2.15"
606+ }
607+ },
608+ "node_modules/postcss-minify-selectors": {
609+ "version": "5.2.1",
610+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
611+ "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
612+ "dev": true,
613+ "dependencies": {
614+ "postcss-selector-parser": "^6.0.5"
615+ },
616+ "engines": {
617+ "node": "^10 || ^12 || >=14.0"
618+ },
619+ "peerDependencies": {
620+ "postcss": "^8.2.15"
621+ }
622+ },
623+ "node_modules/postcss-normalize-charset": {
624+ "version": "5.1.0",
625+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
626+ "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
627+ "dev": true,
628+ "engines": {
629+ "node": "^10 || ^12 || >=14.0"
630+ },
631+ "peerDependencies": {
632+ "postcss": "^8.2.15"
633+ }
634+ },
635+ "node_modules/postcss-normalize-display-values": {
636+ "version": "5.1.0",
637+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
638+ "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
639+ "dev": true,
640+ "dependencies": {
641+ "postcss-value-parser": "^4.2.0"
642+ },
643+ "engines": {
644+ "node": "^10 || ^12 || >=14.0"
645+ },
646+ "peerDependencies": {
647+ "postcss": "^8.2.15"
648+ }
649+ },
650+ "node_modules/postcss-normalize-positions": {
651+ "version": "5.1.1",
652+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
653+ "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
654+ "dev": true,
655+ "dependencies": {
656+ "postcss-value-parser": "^4.2.0"
657+ },
658+ "engines": {
659+ "node": "^10 || ^12 || >=14.0"
660+ },
661+ "peerDependencies": {
662+ "postcss": "^8.2.15"
663+ }
664+ },
665+ "node_modules/postcss-normalize-repeat-style": {
666+ "version": "5.1.1",
667+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
668+ "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
669+ "dev": true,
670+ "dependencies": {
671+ "postcss-value-parser": "^4.2.0"
672+ },
673+ "engines": {
674+ "node": "^10 || ^12 || >=14.0"
675+ },
676+ "peerDependencies": {
677+ "postcss": "^8.2.15"
678+ }
679+ },
680+ "node_modules/postcss-normalize-string": {
681+ "version": "5.1.0",
682+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
683+ "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
684+ "dev": true,
685+ "dependencies": {
686+ "postcss-value-parser": "^4.2.0"
687+ },
688+ "engines": {
689+ "node": "^10 || ^12 || >=14.0"
690+ },
691+ "peerDependencies": {
692+ "postcss": "^8.2.15"
693+ }
694+ },
695+ "node_modules/postcss-normalize-timing-functions": {
696+ "version": "5.1.0",
697+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
698+ "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
699+ "dev": true,
700+ "dependencies": {
701+ "postcss-value-parser": "^4.2.0"
702+ },
703+ "engines": {
704+ "node": "^10 || ^12 || >=14.0"
705+ },
706+ "peerDependencies": {
707+ "postcss": "^8.2.15"
708+ }
709+ },
710+ "node_modules/postcss-normalize-unicode": {
711+ "version": "5.1.1",
712+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
713+ "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
714+ "dev": true,
715+ "dependencies": {
716+ "browserslist": "^4.21.4",
717+ "postcss-value-parser": "^4.2.0"
718+ },
719+ "engines": {
720+ "node": "^10 || ^12 || >=14.0"
721+ },
722+ "peerDependencies": {
723+ "postcss": "^8.2.15"
724+ }
725+ },
726+ "node_modules/postcss-normalize-url": {
727+ "version": "5.1.0",
728+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
729+ "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
730+ "dev": true,
731+ "dependencies": {
732+ "normalize-url": "^6.0.1",
733+ "postcss-value-parser": "^4.2.0"
734+ },
735+ "engines": {
736+ "node": "^10 || ^12 || >=14.0"
737+ },
738+ "peerDependencies": {
739+ "postcss": "^8.2.15"
740+ }
741+ },
742+ "node_modules/postcss-normalize-whitespace": {
743+ "version": "5.1.1",
744+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
745+ "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
746+ "dev": true,
747+ "dependencies": {
748+ "postcss-value-parser": "^4.2.0"
749+ },
750+ "engines": {
751+ "node": "^10 || ^12 || >=14.0"
752+ },
753+ "peerDependencies": {
754+ "postcss": "^8.2.15"
755+ }
756+ },
757+ "node_modules/postcss-ordered-values": {
758+ "version": "5.1.3",
759+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
760+ "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
761+ "dev": true,
762+ "dependencies": {
763+ "cssnano-utils": "^3.1.0",
764+ "postcss-value-parser": "^4.2.0"
765+ },
766+ "engines": {
767+ "node": "^10 || ^12 || >=14.0"
768+ },
769+ "peerDependencies": {
770+ "postcss": "^8.2.15"
771+ }
772+ },
773+ "node_modules/postcss-reduce-initial": {
774+ "version": "5.1.2",
775+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
776+ "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
777+ "dev": true,
778+ "dependencies": {
779+ "browserslist": "^4.21.4",
780+ "caniuse-api": "^3.0.0"
781+ },
782+ "engines": {
783+ "node": "^10 || ^12 || >=14.0"
784+ },
785+ "peerDependencies": {
786+ "postcss": "^8.2.15"
787+ }
788+ },
789+ "node_modules/postcss-reduce-transforms": {
790+ "version": "5.1.0",
791+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
792+ "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
793+ "dev": true,
794+ "dependencies": {
795+ "postcss-value-parser": "^4.2.0"
796+ },
797+ "engines": {
798+ "node": "^10 || ^12 || >=14.0"
799+ },
800+ "peerDependencies": {
801+ "postcss": "^8.2.15"
802+ }
803+ },
804+ "node_modules/postcss-selector-parser": {
805+ "version": "6.0.11",
806+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
807+ "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
808+ "dev": true,
809+ "dependencies": {
810+ "cssesc": "^3.0.0",
811+ "util-deprecate": "^1.0.2"
812+ },
813+ "engines": {
814+ "node": ">=4"
815+ }
816+ },
817+ "node_modules/postcss-svgo": {
818+ "version": "5.1.0",
819+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
820+ "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
821+ "dev": true,
822+ "dependencies": {
823+ "postcss-value-parser": "^4.2.0",
824+ "svgo": "^2.7.0"
825+ },
826+ "engines": {
827+ "node": "^10 || ^12 || >=14.0"
828+ },
829+ "peerDependencies": {
830+ "postcss": "^8.2.15"
831+ }
832+ },
833+ "node_modules/postcss-unique-selectors": {
834+ "version": "5.1.1",
835+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
836+ "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
837+ "dev": true,
838+ "dependencies": {
839+ "postcss-selector-parser": "^6.0.5"
840+ },
841+ "engines": {
842+ "node": "^10 || ^12 || >=14.0"
843+ },
844+ "peerDependencies": {
845+ "postcss": "^8.2.15"
846+ }
847+ },
848+ "node_modules/postcss-value-parser": {
849+ "version": "4.2.0",
850+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
851+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
852+ "dev": true
853+ },
854+ "node_modules/source-map": {
855+ "version": "0.6.1",
856+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
857+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
858+ "dev": true,
859+ "engines": {
860+ "node": ">=0.10.0"
861+ }
862+ },
863+ "node_modules/source-map-js": {
864+ "version": "1.0.2",
865+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
866+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
867+ "dev": true,
868+ "peer": true,
869+ "engines": {
870+ "node": ">=0.10.0"
871+ }
872+ },
873+ "node_modules/stable": {
874+ "version": "0.1.8",
875+ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
876+ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
877+ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
878+ "dev": true
879+ },
880+ "node_modules/stylehacks": {
881+ "version": "5.1.1",
882+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
883+ "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
884+ "dev": true,
885+ "dependencies": {
886+ "browserslist": "^4.21.4",
887+ "postcss-selector-parser": "^6.0.4"
888+ },
889+ "engines": {
890+ "node": "^10 || ^12 || >=14.0"
891+ },
892+ "peerDependencies": {
893+ "postcss": "^8.2.15"
894+ }
895+ },
896+ "node_modules/svgo": {
897+ "version": "2.8.0",
898+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
899+ "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
900+ "dev": true,
901+ "dependencies": {
902+ "@trysound/sax": "0.2.0",
903+ "commander": "^7.2.0",
904+ "css-select": "^4.1.3",
905+ "css-tree": "^1.1.3",
906+ "csso": "^4.2.0",
907+ "picocolors": "^1.0.0",
908+ "stable": "^0.1.8"
909+ },
910+ "bin": {
911+ "svgo": "bin/svgo"
912+ },
913+ "engines": {
914+ "node": ">=10.13.0"
915+ }
916+ },
917+ "node_modules/update-browserslist-db": {
918+ "version": "1.0.10",
919+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
920+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
921+ "dev": true,
922+ "funding": [
923+ {
924+ "type": "opencollective",
925+ "url": "https://opencollective.com/browserslist"
926+ },
927+ {
928+ "type": "tidelift",
929+ "url": "https://tidelift.com/funding/github/npm/browserslist"
930+ }
931+ ],
932+ "dependencies": {
933+ "escalade": "^3.1.1",
934+ "picocolors": "^1.0.0"
935+ },
936+ "bin": {
937+ "browserslist-lint": "cli.js"
938+ },
939+ "peerDependencies": {
940+ "browserslist": ">= 4.21.0"
941+ }
942+ },
943+ "node_modules/util-deprecate": {
944+ "version": "1.0.2",
945+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
946+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
947+ "dev": true
948+ },
949+ "node_modules/yaml": {
950+ "version": "1.10.2",
951+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
952+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
953+ "dev": true,
954+ "engines": {
955+ "node": ">= 6"
956+ }
957+ }
958+ }
959+}
packages.dhall+4−4
@@ -99,8 +99,8 @@ in upstream
99-------------------------------
99-------------------------------
100-}
100-}
101let upstream =
101let upstream =
102− https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220706/packages.dhall
102+ https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20220808/packages.dhall
103− sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031
103+ sha256:60eee64b04ca0013fae3e02a69fc3b176105c6baa2f31865c67cd5f881a412fd
104
104
105in upstream
105in upstream
106 with argonaut-aeson-generic =
106 with argonaut-aeson-generic =
@@ -125,7 +125,7 @@ in upstream
125 ]
125 ]
126 , repo =
126 , repo =
127 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
127 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
128− , version = "purescript-0.14"
128+ , version = "3e388500811ef4b6718fc776789f3be050ad7b57"
129 }
129 }
130 with untagged-union =
130 with untagged-union =
131 { dependencies =
131 { dependencies =
@@ -147,7 +147,7 @@ in upstream
147 }
147 }
148 with literals =
148 with literals =
149 { repo = "https://github.com/ilyakooo0/purescript-literals.git"
149 { repo = "https://github.com/ilyakooo0/purescript-literals.git"
150− , version = "iko/_null"
150+ , version = "6875fb28026595cfb780318305a77e79b098bb01"
151 , dependencies =
151 , dependencies =
152 [ "assert"
152 [ "assert"
153 , "effect"
153 , "effect"
registry.datnew file

Binary file not shown.

spago-packages.nixnew file+1102
@@ -0,0 +1,1102 @@
1+# This file was generated by Spago2Nix
2+
3+{ pkgs ? import <nixpkgs> {} }:
4+
5+let
6+ inputs = {
7+
8+ "aff" = pkgs.stdenv.mkDerivation {
9+ name = "aff";
10+ version = "v7.0.0";
11+ src = pkgs.fetchgit {
12+ url = "https://github.com/purescript-contrib/purescript-aff.git";
13+ rev = "2d44d9f9d0d6a416a4103fba2fb39e5160f80e36";
14+ sha256 = "1d38l16s8hvfqq5w40w21j5089svdiggkraw3rwrl6ml7lijjqx4";
15+ };
16+ phases = "installPhase";
17+ installPhase = "ln -s $src $out";
18+ };
19+
20+ "aff-promise" = pkgs.stdenv.mkDerivation {
21+ name = "aff-promise";
22+ version = "v4.0.0";
23+ src = pkgs.fetchgit {
24+ url = "https://github.com/nwolverson/purescript-aff-promise.git";
25+ rev = "3aa74e68e3e4c3e38d821375703e0b2f49d831eb";
26+ sha256 = "10gv3b05dxnlmr9zmigqs1hz4xqrx7da30aqh12kqg3zrg49k9cc";
27+ };
28+ phases = "installPhase";
29+ installPhase = "ln -s $src $out";
30+ };
31+
32+ "argonaut" = pkgs.stdenv.mkDerivation {
33+ name = "argonaut";
34+ version = "v9.0.0";
35+ src = pkgs.fetchgit {
36+ url = "https://github.com/purescript-contrib/purescript-argonaut.git";
37+ rev = "7505a47f2edb0c9cacaac4f11dcedf4723a3e9c8";
38+ sha256 = "18yxhlrwri6q858krz1klyq29fx8nvfm16c04wm2rn91mgyasn6x";
39+ };
40+ phases = "installPhase";
41+ installPhase = "ln -s $src $out";
42+ };
43+
44+ "argonaut-aeson-generic" = pkgs.stdenv.mkDerivation {
45+ name = "argonaut-aeson-generic";
46+ version = "3e388500811ef4b6718fc776789f3be050ad7b57";
47+ src = pkgs.fetchgit {
48+ url = "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git";
49+ rev = "3e388500811ef4b6718fc776789f3be050ad7b57";
50+ sha256 = "0yhcxxz5lpgwxxsx93kmr884rk5nrvgy7lmpaixazqps2a2q0fhc";
51+ };
52+ phases = "installPhase";
53+ installPhase = "ln -s $src $out";
54+ };
55+
56+ "argonaut-codecs" = pkgs.stdenv.mkDerivation {
57+ name = "argonaut-codecs";
58+ version = "v9.0.0";
59+ src = pkgs.fetchgit {
60+ url = "https://github.com/purescript-contrib/purescript-argonaut-codecs.git";
61+ rev = "176a5dddae28fda9a2ff31ed4bf1efcc148513a4";
62+ sha256 = "08ys445222syysxh24la8bc8l20lwwqi4vdfa3ph2a60l1lx3kp8";
63+ };
64+ phases = "installPhase";
65+ installPhase = "ln -s $src $out";
66+ };
67+
68+ "argonaut-core" = pkgs.stdenv.mkDerivation {
69+ name = "argonaut-core";
70+ version = "v7.0.0";
71+ src = pkgs.fetchgit {
72+ url = "https://github.com/purescript-contrib/purescript-argonaut-core.git";
73+ rev = "68da81dd80ec36d3b013eff46dc067a972c22e5d";
74+ sha256 = "0hxl17ddbflkk0hchjgk5xj6j9fwp3b182w4g073p0dwscdl4f08";
75+ };
76+ phases = "installPhase";
77+ installPhase = "ln -s $src $out";
78+ };
79+
80+ "argonaut-generic" = pkgs.stdenv.mkDerivation {
81+ name = "argonaut-generic";
82+ version = "v8.0.0";
83+ src = pkgs.fetchgit {
84+ url = "https://github.com/purescript-contrib/purescript-argonaut-generic.git";
85+ rev = "2df4080f036762df91a24b22842e389395ef0bdd";
86+ sha256 = "0f3bkmv37nk89vscvivdr971sjydqiw7lwk9hfdfms05id8jdjkw";
87+ };
88+ phases = "installPhase";
89+ installPhase = "ln -s $src $out";
90+ };
91+
92+ "argonaut-traversals" = pkgs.stdenv.mkDerivation {
93+ name = "argonaut-traversals";
94+ version = "v10.0.0";
95+ src = pkgs.fetchgit {
96+ url = "https://github.com/purescript-contrib/purescript-argonaut-traversals.git";
97+ rev = "8d2403d8d57afb568933dbb36063d5670ce770a0";
98+ sha256 = "0v965bcl1hdp1hacbzxcm35kxxclixj0kpsdm816gs7m548z6l43";
99+ };
100+ phases = "installPhase";
101+ installPhase = "ln -s $src $out";
102+ };
103+
104+ "arraybuffer" = pkgs.stdenv.mkDerivation {
105+ name = "arraybuffer";
106+ version = "v13.0.0";
107+ src = pkgs.fetchgit {
108+ url = "https://github.com/purescript-contrib/purescript-arraybuffer.git";
109+ rev = "3b5ba3040f060057e864805546a50f319853517d";
110+ sha256 = "0cfi15r95zd7ikvs8v3pf2i3rzxjqxk7209j96d8qs6jzqlypbwd";
111+ };
112+ phases = "installPhase";
113+ installPhase = "ln -s $src $out";
114+ };
115+
116+ "arraybuffer-types" = pkgs.stdenv.mkDerivation {
117+ name = "arraybuffer-types";
118+ version = "v3.0.2";
119+ src = pkgs.fetchgit {
120+ url = "https://github.com/purescript-contrib/purescript-arraybuffer-types.git";
121+ rev = "9b0b7a0f9ee034e039f3d3a2a9c3f74eb7c9264a";
122+ sha256 = "1q3111jk6jj64bxy86lc7ik6q5mgdz87bc086wj5rm57dmlivlxx";
123+ };
124+ phases = "installPhase";
125+ installPhase = "ln -s $src $out";
126+ };
127+
128+ "arrays" = pkgs.stdenv.mkDerivation {
129+ name = "arrays";
130+ version = "v7.0.0";
131+ src = pkgs.fetchgit {
132+ url = "https://github.com/purescript/purescript-arrays.git";
133+ rev = "d20bae2f76db6cf29b7b75e26e82b8a54c32295e";
134+ sha256 = "1ciz796yrdfnqiskjjhh6r3bn9mks1dm5zin7kwzh1nfy1i4w9pz";
135+ };
136+ phases = "installPhase";
137+ installPhase = "ln -s $src $out";
138+ };
139+
140+ "assert" = pkgs.stdenv.mkDerivation {
141+ name = "assert";
142+ version = "v6.0.0";
143+ src = pkgs.fetchgit {
144+ url = "https://github.com/purescript/purescript-assert.git";
145+ rev = "27c0edb57d2ee497eb5fab664f5601c35b613eda";
146+ sha256 = "02qpcsijil64qq4kcikv2ynxl85zkryvv0vmzjm6752956ymzi0j";
147+ };
148+ phases = "installPhase";
149+ installPhase = "ln -s $src $out";
150+ };
151+
152+ "avar" = pkgs.stdenv.mkDerivation {
153+ name = "avar";
154+ version = "v5.0.0";
155+ src = pkgs.fetchgit {
156+ url = "https://github.com/purescript-contrib/purescript-avar.git";
157+ rev = "d00f5784d9cc8f079babd62740f5c52b87e5caa5";
158+ sha256 = "132g0ccmjr8328xpaycww8b5wmx1vi3li4irxxqcjk5wi47i8c9h";
159+ };
160+ phases = "installPhase";
161+ installPhase = "ln -s $src $out";
162+ };
163+
164+ "bifunctors" = pkgs.stdenv.mkDerivation {
165+ name = "bifunctors";
166+ version = "v6.0.0";
167+ src = pkgs.fetchgit {
168+ url = "https://github.com/purescript/purescript-bifunctors.git";
169+ rev = "16ba2fb6dd7f05528ebd9e2f9ca3a068b325e5b3";
170+ sha256 = "1qz5aaiq5w4g1rhjb63z47h6nqhw8hw23x1zw5alam047pq4vfkg";
171+ };
172+ phases = "installPhase";
173+ installPhase = "ln -s $src $out";
174+ };
175+
176+ "catenable-lists" = pkgs.stdenv.mkDerivation {
177+ name = "catenable-lists";
178+ version = "v7.0.0";
179+ src = pkgs.fetchgit {
180+ url = "https://github.com/purescript/purescript-catenable-lists.git";
181+ rev = "09abe1f4888bc00841ad2b59e56a9e7ce7ebd4ab";
182+ sha256 = "0fn6caspdnb0nrlj31sy7d5hxrhndfz7a2b9wfkxf5bv5vpjyawb";
183+ };
184+ phases = "installPhase";
185+ installPhase = "ln -s $src $out";
186+ };
187+
188+ "console" = pkgs.stdenv.mkDerivation {
189+ name = "console";
190+ version = "v6.0.0";
191+ src = pkgs.fetchgit {
192+ url = "https://github.com/purescript/purescript-console.git";
193+ rev = "3b83d7b792d03872afeea5e62b4f686ab0f09842";
194+ sha256 = "0fr5l1myhscp910mybp04cg6g8f2hy3ikjfc8fkqlb2dm2cqzdfs";
195+ };
196+ phases = "installPhase";
197+ installPhase = "ln -s $src $out";
198+ };
199+
200+ "const" = pkgs.stdenv.mkDerivation {
201+ name = "const";
202+ version = "v6.0.0";
203+ src = pkgs.fetchgit {
204+ url = "https://github.com/purescript/purescript-const.git";
205+ rev = "ab9570cf2b6e67f7e441178211db1231cfd75c37";
206+ sha256 = "0mcpwqqf5bcwxlzk53smvsjjz1ymlnq0ypphg6sc83ibw70g64f6";
207+ };
208+ phases = "installPhase";
209+ installPhase = "ln -s $src $out";
210+ };
211+
212+ "contravariant" = pkgs.stdenv.mkDerivation {
213+ name = "contravariant";
214+ version = "v6.0.0";
215+ src = pkgs.fetchgit {
216+ url = "https://github.com/purescript/purescript-contravariant.git";
217+ rev = "9ad3e105b8855bcc25f4e0893c784789d05a58de";
218+ sha256 = "1xkd3rfs8v20w7mj6sj6dmhfb2vb1zpmjv245xj1rahk3g16qm7m";
219+ };
220+ phases = "installPhase";
221+ installPhase = "ln -s $src $out";
222+ };
223+
224+ "control" = pkgs.stdenv.mkDerivation {
225+ name = "control";
226+ version = "v6.0.0";
227+ src = pkgs.fetchgit {
228+ url = "https://github.com/purescript/purescript-control.git";
229+ rev = "a6033808790879a17b2729e73747a9ed3fb2264e";
230+ sha256 = "05sdgywprwpav62d2bvlb80yd39brxhnmhhl1f116mm9hw5clqia";
231+ };
232+ phases = "installPhase";
233+ installPhase = "ln -s $src $out";
234+ };
235+
236+ "datetime" = pkgs.stdenv.mkDerivation {
237+ name = "datetime";
238+ version = "v6.0.0";
239+ src = pkgs.fetchgit {
240+ url = "https://github.com/purescript/purescript-datetime.git";
241+ rev = "a6a0cf1b0324964ad1854bc3377ed8766ba90e6f";
242+ sha256 = "1idjljxy8rjlw3wiypk1c9cc1fc4g9kf7drzdy6a0yjdg7504gad";
243+ };
244+ phases = "installPhase";
245+ installPhase = "ln -s $src $out";
246+ };
247+
248+ "datetime-parsing" = pkgs.stdenv.mkDerivation {
249+ name = "datetime-parsing";
250+ version = "v0.2.0";
251+ src = pkgs.fetchgit {
252+ url = "https://github.com/flounders/purescript-datetime-parsing.git";
253+ rev = "aa7d336c2681501dbe343adcbbf8f2ee51b23932";
254+ sha256 = "1w0lfs64qz1hs7zijyzpvqlp2z06wmbz4jp8cjsnw9yj46jsy8nh";
255+ };
256+ phases = "installPhase";
257+ installPhase = "ln -s $src $out";
258+ };
259+
260+ "distributive" = pkgs.stdenv.mkDerivation {
261+ name = "distributive";
262+ version = "v6.0.0";
263+ src = pkgs.fetchgit {
264+ url = "https://github.com/purescript/purescript-distributive.git";
265+ rev = "6005e513642e855ebf6f884d24a35c2803ca252a";
266+ sha256 = "0x6cfsx9ff66kvw9l10pxd5vv16r91mmfxrfc68w1xfdfi81lhdx";
267+ };
268+ phases = "installPhase";
269+ installPhase = "ln -s $src $out";
270+ };
271+
272+ "effect" = pkgs.stdenv.mkDerivation {
273+ name = "effect";
274+ version = "v4.0.0";
275+ src = pkgs.fetchgit {
276+ url = "https://github.com/purescript/purescript-effect.git";
277+ rev = "a192ddb923027d426d6ea3d8deb030c9aa7c7dda";
278+ sha256 = "0aa10lc6h9mlf4xf3g3ziig7v6kxdqvbh20kma8ay59w0b1bhmj1";
279+ };
280+ phases = "installPhase";
281+ installPhase = "ln -s $src $out";
282+ };
283+
284+ "either" = pkgs.stdenv.mkDerivation {
285+ name = "either";
286+ version = "v6.1.0";
287+ src = pkgs.fetchgit {
288+ url = "https://github.com/purescript/purescript-either.git";
289+ rev = "af655a04ed2fd694b6688af39ee20d7907ad0763";
290+ sha256 = "05zps4klvgmvlm06f4hrycssm8q0pysbqnjsrk26lfvmid6mmg63";
291+ };
292+ phases = "installPhase";
293+ installPhase = "ln -s $src $out";
294+ };
295+
296+ "enums" = pkgs.stdenv.mkDerivation {
297+ name = "enums";
298+ version = "v6.0.0";
299+ src = pkgs.fetchgit {
300+ url = "https://github.com/purescript/purescript-enums.git";
301+ rev = "bae47961d401f9acf88da38f32e87403e5f0cf2f";
302+ sha256 = "08sl5q07hv6v2008dcpg4mxiqwk9z0fhq67k0bgkxvlbsmar7khg";
303+ };
304+ phases = "installPhase";
305+ installPhase = "ln -s $src $out";
306+ };
307+
308+ "exceptions" = pkgs.stdenv.mkDerivation {
309+ name = "exceptions";
310+ version = "v6.0.0";
311+ src = pkgs.fetchgit {
312+ url = "https://github.com/purescript/purescript-exceptions.git";
313+ rev = "afab3c07c820bb49b6c5be50049db46a964a6161";
314+ sha256 = "04xrbrcjwsv8gj465ygrlqhv1majxmr6m242iazg6apxlr89hgvg";
315+ };
316+ phases = "installPhase";
317+ installPhase = "ln -s $src $out";
318+ };
319+
320+ "exists" = pkgs.stdenv.mkDerivation {
321+ name = "exists";
322+ version = "v6.0.0";
323+ src = pkgs.fetchgit {
324+ url = "https://github.com/purescript/purescript-exists.git";
325+ rev = "f765b4ace7869c27b9c05949e18c843881f9173b";
326+ sha256 = "0xnxi8fsmi15wrpq5d99gg94glk8a45hpx7pf4pmxg5r6rn1vjvx";
327+ };
328+ phases = "installPhase";
329+ installPhase = "ln -s $src $out";
330+ };
331+
332+ "float32" = pkgs.stdenv.mkDerivation {
333+ name = "float32";
334+ version = "v2.0.0";
335+ src = pkgs.fetchgit {
336+ url = "https://github.com/purescript-contrib/purescript-float32.git";
337+ rev = "3b70984d823c12c1b84a45a9b3d45199a4fcc67d";
338+ sha256 = "0a1lvga6bbhbb8g3crp3fr0kmpkzjmffhfq71ig6flin13xijgys";
339+ };
340+ phases = "installPhase";
341+ installPhase = "ln -s $src $out";
342+ };
343+
344+ "foldable-traversable" = pkgs.stdenv.mkDerivation {
345+ name = "foldable-traversable";
346+ version = "v6.0.0";
347+ src = pkgs.fetchgit {
348+ url = "https://github.com/purescript/purescript-foldable-traversable.git";
349+ rev = "b3926f870532d287ea59e2d5cd3873b81ef2a93a";
350+ sha256 = "0xg8qvyc6r9wqgy7wnw1rjqljl4wpgdrkxsm5x9rsagj2k5brxip";
351+ };
352+ phases = "installPhase";
353+ installPhase = "ln -s $src $out";
354+ };
355+
356+ "foreign" = pkgs.stdenv.mkDerivation {
357+ name = "foreign";
358+ version = "v7.0.0";
359+ src = pkgs.fetchgit {
360+ url = "https://github.com/purescript/purescript-foreign.git";
361+ rev = "2dd222d1ec7363fa0a0a7adb0d8eaf81bb7006dd";
362+ sha256 = "0ycjaal8b2rsg3zl3b7acv166vjfdvziiza3fs8nhfw0rx0xmm1m";
363+ };
364+ phases = "installPhase";
365+ installPhase = "ln -s $src $out";
366+ };
367+
368+ "foreign-object" = pkgs.stdenv.mkDerivation {
369+ name = "foreign-object";
370+ version = "v4.0.0";
371+ src = pkgs.fetchgit {
372+ url = "https://github.com/purescript/purescript-foreign-object.git";
373+ rev = "28a635827a9a6c251df73f68874070d51fe9f756";
374+ sha256 = "0ql0vgndcb5dqaqz81mvrqkrl5c2rlgxpydgdiiaxmmfxvxwi439";
375+ };
376+ phases = "installPhase";
377+ installPhase = "ln -s $src $out";
378+ };
379+
380+ "free" = pkgs.stdenv.mkDerivation {
381+ name = "free";
382+ version = "v7.0.0";
383+ src = pkgs.fetchgit {
384+ url = "https://github.com/purescript/purescript-free.git";
385+ rev = "e2d8fa8023a864363857834e11393483bced5e38";
386+ sha256 = "0gyy0k297m26gw76w7kx8k1zjczlqbqb3nmh2h4gb3r84gl05g6r";
387+ };
388+ phases = "installPhase";
389+ installPhase = "ln -s $src $out";
390+ };
391+
392+ "functions" = pkgs.stdenv.mkDerivation {
393+ name = "functions";
394+ version = "v6.0.0";
395+ src = pkgs.fetchgit {
396+ url = "https://github.com/purescript/purescript-functions.git";
397+ rev = "f626f20580483977c5b27a01aac6471e28aff367";
398+ sha256 = "1bsf2y8hx103a92yrc6m05q2dv51ckag1srd7q5n9vaf2k9byrj9";
399+ };
400+ phases = "installPhase";
401+ installPhase = "ln -s $src $out";
402+ };
403+
404+ "functors" = pkgs.stdenv.mkDerivation {
405+ name = "functors";
406+ version = "v5.0.0";
407+ src = pkgs.fetchgit {
408+ url = "https://github.com/purescript/purescript-functors.git";
409+ rev = "022ffd7a2a7ec12080314f3d217b400674a247b4";
410+ sha256 = "1pnw3r1nk0rx8mrw3ajjml40zvx9v26qrqwj77pz7hpwxigxfbff";
411+ };
412+ phases = "installPhase";
413+ installPhase = "ln -s $src $out";
414+ };
415+
416+ "gen" = pkgs.stdenv.mkDerivation {
417+ name = "gen";
418+ version = "v4.0.0";
419+ src = pkgs.fetchgit {
420+ url = "https://github.com/purescript/purescript-gen.git";
421+ rev = "9fbcc2a1261c32e30d79c5418edef4d96fe76931";
422+ sha256 = "0kajzcp9a8dmqvb3ga27hlf18m2smbp98a0bmzzb9vv156ckw0b4";
423+ };
424+ phases = "installPhase";
425+ installPhase = "ln -s $src $out";
426+ };
427+
428+ "identity" = pkgs.stdenv.mkDerivation {
429+ name = "identity";
430+ version = "v6.0.0";
431+ src = pkgs.fetchgit {
432+ url = "https://github.com/purescript/purescript-identity.git";
433+ rev = "ef6768f8a52ab0bc943a85f5761ba07c257f639f";
434+ sha256 = "1lh0pkwwdyd8q9bgf4l1m96gdnardki4r2s359cnhrwp94b43fyr";
435+ };
436+ phases = "installPhase";
437+ installPhase = "ln -s $src $out";
438+ };
439+
440+ "integers" = pkgs.stdenv.mkDerivation {
441+ name = "integers";
442+ version = "v6.0.0";
443+ src = pkgs.fetchgit {
444+ url = "https://github.com/purescript/purescript-integers.git";
445+ rev = "54d712b25c594833083d15dc9ff2418eb9c52822";
446+ sha256 = "04j9sqvkgyxxvbm8jfc2118wzmgswn8jva9fysgbsnmsg9vcxfm6";
447+ };
448+ phases = "installPhase";
449+ installPhase = "ln -s $src $out";
450+ };
451+
452+ "invariant" = pkgs.stdenv.mkDerivation {
453+ name = "invariant";
454+ version = "v6.0.0";
455+ src = pkgs.fetchgit {
456+ url = "https://github.com/purescript/purescript-invariant.git";
457+ rev = "1d2a196d51e90623adb88496c2cfd759c6736894";
458+ sha256 = "07rqag47ykdiqc5yfbc1a64ijaqliljhcw1vgbv81m7ljzzd2xna";
459+ };
460+ phases = "installPhase";
461+ installPhase = "ln -s $src $out";
462+ };
463+
464+ "js-date" = pkgs.stdenv.mkDerivation {
465+ name = "js-date";
466+ version = "v8.0.0";
467+ src = pkgs.fetchgit {
468+ url = "https://github.com/purescript-contrib/purescript-js-date.git";
469+ rev = "1ea020316946cc4b87195bca9c54d0c16abaa490";
470+ sha256 = "03535g8hc4bzyzig25hf75dls9ypsgrdk2nc45h8zw4xyy4ha0gz";
471+ };
472+ phases = "installPhase";
473+ installPhase = "ln -s $src $out";
474+ };
475+
476+ "js-timers" = pkgs.stdenv.mkDerivation {
477+ name = "js-timers";
478+ version = "v6.1.0";
479+ src = pkgs.fetchgit {
480+ url = "https://github.com/purescript-contrib/purescript-js-timers.git";
481+ rev = "7cb728b3e6dc29f355143617e6e9ac770ecd9273";
482+ sha256 = "0dm933kqhwgmpkylgrlpjplidn8biic8jlzs4l4nny4a1z20mr61";
483+ };
484+ phases = "installPhase";
485+ installPhase = "ln -s $src $out";
486+ };
487+
488+ "lazy" = pkgs.stdenv.mkDerivation {
489+ name = "lazy";
490+ version = "v6.0.0";
491+ src = pkgs.fetchgit {
492+ url = "https://github.com/purescript/purescript-lazy.git";
493+ rev = "48347841226b27af5205a1a8ec71e27a93ce86fd";
494+ sha256 = "0dxlc3b2bdqsri45i7a9bvwvv0gg43cc1gq61zi41f290a29ny2q";
495+ };
496+ phases = "installPhase";
497+ installPhase = "ln -s $src $out";
498+ };
499+
500+ "lcg" = pkgs.stdenv.mkDerivation {
501+ name = "lcg";
502+ version = "v4.0.0";
503+ src = pkgs.fetchgit {
504+ url = "https://github.com/purescript/purescript-lcg.git";
505+ rev = "67c6c6483a563a59ae036d9dca0f1be2835326a5";
506+ sha256 = "1shzn6zvc1cxd7v0bvfsk7x3xf59vxby8c5lfjvd746r6396zn65";
507+ };
508+ phases = "installPhase";
509+ installPhase = "ln -s $src $out";
510+ };
511+
512+ "lists" = pkgs.stdenv.mkDerivation {
513+ name = "lists";
514+ version = "v7.0.0";
515+ src = pkgs.fetchgit {
516+ url = "https://github.com/purescript/purescript-lists.git";
517+ rev = "b113451e5b41cad87d669a3165f955c71cd863e2";
518+ sha256 = "1mg7vy44k8jlqkri7x4ikciixx92b17wi35887x09dwdsicjf0sx";
519+ };
520+ phases = "installPhase";
521+ installPhase = "ln -s $src $out";
522+ };
523+
524+ "literals" = pkgs.stdenv.mkDerivation {
525+ name = "literals";
526+ version = "6875fb28026595cfb780318305a77e79b098bb01";
527+ src = pkgs.fetchgit {
528+ url = "https://github.com/ilyakooo0/purescript-literals.git";
529+ rev = "6875fb28026595cfb780318305a77e79b098bb01";
530+ sha256 = "0s45cdgpdhni3rj7irr8pch46yxwmdcmj2nrg1vpkvygydkikdmb";
531+ };
532+ phases = "installPhase";
533+ installPhase = "ln -s $src $out";
534+ };
535+
536+ "maybe" = pkgs.stdenv.mkDerivation {
537+ name = "maybe";
538+ version = "v6.0.0";
539+ src = pkgs.fetchgit {
540+ url = "https://github.com/purescript/purescript-maybe.git";
541+ rev = "c6f98ac1088766287106c5d9c8e30e7648d36786";
542+ sha256 = "0n33g8c579vcs06ii0r7f14jjdwzjw7p583w4nmrv9h442q2cvlz";
543+ };
544+ phases = "installPhase";
545+ installPhase = "ln -s $src $out";
546+ };
547+
548+ "media-types" = pkgs.stdenv.mkDerivation {
549+ name = "media-types";
550+ version = "v6.0.0";
551+ src = pkgs.fetchgit {
552+ url = "https://github.com/purescript-contrib/purescript-media-types.git";
553+ rev = "af853de226592f319a953637069a943dd261cba3";
554+ sha256 = "0bxpsd2yw3zg58ay649v3xld7n17n7j7n9y8l841cjih9bc7ccxx";
555+ };
556+ phases = "installPhase";
557+ installPhase = "ln -s $src $out";
558+ };
559+
560+ "newtype" = pkgs.stdenv.mkDerivation {
561+ name = "newtype";
562+ version = "v5.0.0";
563+ src = pkgs.fetchgit {
564+ url = "https://github.com/purescript/purescript-newtype.git";
565+ rev = "29d8e6dd77aec2c975c948364ec3faf26e14ee7b";
566+ sha256 = "0kfc644zn2f9rpx8fy4gjvmz04k9mv1c2xrqis8m3s0ydnhbffd7";
567+ };
568+ phases = "installPhase";
569+ installPhase = "ln -s $src $out";
570+ };
571+
572+ "nonempty" = pkgs.stdenv.mkDerivation {
573+ name = "nonempty";
574+ version = "v7.0.0";
575+ src = pkgs.fetchgit {
576+ url = "https://github.com/purescript/purescript-nonempty.git";
577+ rev = "28150ecc7419238b187abd609a92a645273348bb";
578+ sha256 = "1mwdc2sny2ygp67kyk7h2493vq7syf7j18s3d9a5gwf1y7jlf7gh";
579+ };
580+ phases = "installPhase";
581+ installPhase = "ln -s $src $out";
582+ };
583+
584+ "now" = pkgs.stdenv.mkDerivation {
585+ name = "now";
586+ version = "v6.0.0";
587+ src = pkgs.fetchgit {
588+ url = "https://github.com/purescript-contrib/purescript-now.git";
589+ rev = "b5ffed2381e5fefc063f484e607e8499e79eaf32";
590+ sha256 = "01khmqs4iz7dhlvd43zxj19q0c0vvqf1m128vgvgxz5mv430zfgr";
591+ };
592+ phases = "installPhase";
593+ installPhase = "ln -s $src $out";
594+ };
595+
596+ "nullable" = pkgs.stdenv.mkDerivation {
597+ name = "nullable";
598+ version = "v6.0.0";
599+ src = pkgs.fetchgit {
600+ url = "https://github.com/purescript-contrib/purescript-nullable.git";
601+ rev = "3202744c6c65e8d1fbba7f4256a1c482078e7fb5";
602+ sha256 = "10s3b54pjmr2rxlyzvc8yc99k1dpn0qrijnc4y65yj5z6bxf2791";
603+ };
604+ phases = "installPhase";
605+ installPhase = "ln -s $src $out";
606+ };
607+
608+ "numbers" = pkgs.stdenv.mkDerivation {
609+ name = "numbers";
610+ version = "v9.0.0";
611+ src = pkgs.fetchgit {
612+ url = "https://github.com/purescript/purescript-numbers.git";
613+ rev = "2a53528f18f9415334bae28e7bb3cf3be86342c2";
614+ sha256 = "1h4v6ir1hq6gvkk0kla38vlcd6bv73xkc8pv71jygwqxrdwfxxgw";
615+ };
616+ phases = "installPhase";
617+ installPhase = "ln -s $src $out";
618+ };
619+
620+ "ordered-collections" = pkgs.stdenv.mkDerivation {
621+ name = "ordered-collections";
622+ version = "v3.0.0";
623+ src = pkgs.fetchgit {
624+ url = "https://github.com/purescript/purescript-ordered-collections.git";
625+ rev = "9826b7632d0d0a691173bde308a634195f42a419";
626+ sha256 = "1wk8mcn1zsxi0yk9ybas2v7m0drb492806gld1xxxic8x1i19ws0";
627+ };
628+ phases = "installPhase";
629+ installPhase = "ln -s $src $out";
630+ };
631+
632+ "orders" = pkgs.stdenv.mkDerivation {
633+ name = "orders";
634+ version = "v6.0.0";
635+ src = pkgs.fetchgit {
636+ url = "https://github.com/purescript/purescript-orders.git";
637+ rev = "f86db621ec5eef1274145f8b1fd8ebbfe0ed4a2c";
638+ sha256 = "14fjls3v14ia6fr2w4p0i5dqv10zk343wg28m4c77a17jmvbs9r4";
639+ };
640+ phases = "installPhase";
641+ installPhase = "ln -s $src $out";
642+ };
643+
644+ "parallel" = pkgs.stdenv.mkDerivation {
645+ name = "parallel";
646+ version = "v6.0.0";
647+ src = pkgs.fetchgit {
648+ url = "https://github.com/purescript/purescript-parallel.git";
649+ rev = "85290dca837771ac4870071008c933d315ef678f";
650+ sha256 = "0bpwvzq4dn7vx5mfj12w1zvvnamc6w2c45c2v54b9yw36snnwpcf";
651+ };
652+ phases = "installPhase";
653+ installPhase = "ln -s $src $out";
654+ };
655+
656+ "parsing" = pkgs.stdenv.mkDerivation {
657+ name = "parsing";
658+ version = "v9.1.0";
659+ src = pkgs.fetchgit {
660+ url = "https://github.com/purescript-contrib/purescript-parsing.git";
661+ rev = "44ef16b7cfb7cedc3d518c00ef10c6cb73ff4fc8";
662+ sha256 = "1wkk3p1w2k1cvby4airyxgavk1djfzvzc2ch6b8951b592rj8lkr";
663+ };
664+ phases = "installPhase";
665+ installPhase = "ln -s $src $out";
666+ };
667+
668+ "partial" = pkgs.stdenv.mkDerivation {
669+ name = "partial";
670+ version = "v4.0.0";
671+ src = pkgs.fetchgit {
672+ url = "https://github.com/purescript/purescript-partial.git";
673+ rev = "0fa0646f5ea1ec5f0c46dcbd770c705a6c9ad3ec";
674+ sha256 = "04s1h0r3slyd8kcamhqqrr6piksl9y76nmf2418j0ifhp16qwxmm";
675+ };
676+ phases = "installPhase";
677+ installPhase = "ln -s $src $out";
678+ };
679+
680+ "prelude" = pkgs.stdenv.mkDerivation {
681+ name = "prelude";
682+ version = "v6.0.0";
683+ src = pkgs.fetchgit {
684+ url = "https://github.com/purescript/purescript-prelude.git";
685+ rev = "32787f4399c92459c41602131a5858559eb868c5";
686+ sha256 = "0jkbckxw2640gmfi2f8bpf2zwnay2dxy8n24rvxl3w3dihcgd9qc";
687+ };
688+ phases = "installPhase";
689+ installPhase = "ln -s $src $out";
690+ };
691+
692+ "profunctor" = pkgs.stdenv.mkDerivation {
693+ name = "profunctor";
694+ version = "v6.0.0";
695+ src = pkgs.fetchgit {
696+ url = "https://github.com/purescript/purescript-profunctor.git";
697+ rev = "0a966a14e7b0c827d44657dc1710cdc712d2e034";
698+ sha256 = "07rhk8micbc74my6s53xbj2smvvwah2w1drqrcmcnncxdks3lqg3";
699+ };
700+ phases = "installPhase";
701+ installPhase = "ln -s $src $out";
702+ };
703+
704+ "profunctor-lenses" = pkgs.stdenv.mkDerivation {
705+ name = "profunctor-lenses";
706+ version = "v8.0.0";
707+ src = pkgs.fetchgit {
708+ url = "https://github.com/purescript-contrib/purescript-profunctor-lenses.git";
709+ rev = "973d567afe458fd802cf4f0d9725b6dc35ad9297";
710+ sha256 = "0axz7nznn12iqh6sn9qjg6i8qglpdsqgkp30ndwdj98czcnbnybv";
711+ };
712+ phases = "installPhase";
713+ installPhase = "ln -s $src $out";
714+ };
715+
716+ "psci-support" = pkgs.stdenv.mkDerivation {
717+ name = "psci-support";
718+ version = "v6.0.0";
719+ src = pkgs.fetchgit {
720+ url = "https://github.com/purescript/purescript-psci-support.git";
721+ rev = "897cdb543548cb6078d69b6413b54841404eda72";
722+ sha256 = "1ix53r8avkn3fw72mngwzw7v6c6mv7j4miw5mrgjrh9hb8p2ydl1";
723+ };
724+ phases = "installPhase";
725+ installPhase = "ln -s $src $out";
726+ };
727+
728+ "quickcheck" = pkgs.stdenv.mkDerivation {
729+ name = "quickcheck";
730+ version = "v8.0.1";
731+ src = pkgs.fetchgit {
732+ url = "https://github.com/purescript/purescript-quickcheck.git";
733+ rev = "bf5029f97e6c0d7552d3a08d2ab793a19e2c5e3d";
734+ sha256 = "142dvh57fl8b6i7mm37a38v7vkc1znbqz6l6wqa704m53hkikvyd";
735+ };
736+ phases = "installPhase";
737+ installPhase = "ln -s $src $out";
738+ };
739+
740+ "random" = pkgs.stdenv.mkDerivation {
741+ name = "random";
742+ version = "v6.0.0";
743+ src = pkgs.fetchgit {
744+ url = "https://github.com/purescript/purescript-random.git";
745+ rev = "9540bc965a9596da02fefd9949418bb19c92533a";
746+ sha256 = "0qz14qviz5053j9h1jwpk0gnn692hcdx6fp90wckzrnw31d53174";
747+ };
748+ phases = "installPhase";
749+ installPhase = "ln -s $src $out";
750+ };
751+
752+ "record" = pkgs.stdenv.mkDerivation {
753+ name = "record";
754+ version = "v4.0.0";
755+ src = pkgs.fetchgit {
756+ url = "https://github.com/purescript/purescript-record.git";
757+ rev = "c89cd1ada6b636692571fc374196b1c39c4c9f70";
758+ sha256 = "1g7s2h1as5cz824wpm0jhjprrh66shha5i4gq37q73yw0s5p2ahm";
759+ };
760+ phases = "installPhase";
761+ installPhase = "ln -s $src $out";
762+ };
763+
764+ "refs" = pkgs.stdenv.mkDerivation {
765+ name = "refs";
766+ version = "v6.0.0";
767+ src = pkgs.fetchgit {
768+ url = "https://github.com/purescript/purescript-refs.git";
769+ rev = "f8e6216da4cb9309fde1f20cd6f69ac3a3b7f9e8";
770+ sha256 = "09bvfxhjfwfwv55py45s71maazwrr68k0rk4v8ynfqv91h34319h";
771+ };
772+ phases = "installPhase";
773+ installPhase = "ln -s $src $out";
774+ };
775+
776+ "run" = pkgs.stdenv.mkDerivation {
777+ name = "run";
778+ version = "v5.0.0";
779+ src = pkgs.fetchgit {
780+ url = "https://github.com/natefaubion/purescript-run.git";
781+ rev = "abec7c343e92154d44b9dafd52b91ee82d32a870";
782+ sha256 = "059xhjxjfs1w2hmgld53kciyfws3pmjyz75w836ph99rqr4aczny";
783+ };
784+ phases = "installPhase";
785+ installPhase = "ln -s $src $out";
786+ };
787+
788+ "safe-coerce" = pkgs.stdenv.mkDerivation {
789+ name = "safe-coerce";
790+ version = "v2.0.0";
791+ src = pkgs.fetchgit {
792+ url = "https://github.com/purescript/purescript-safe-coerce.git";
793+ rev = "7fa799ae80a38b8d948efcb52608e58e198b3da7";
794+ sha256 = "00m4l733gpl0153cbl6n5kly7jr8ids399apza2rbczif40brp9g";
795+ };
796+ phases = "installPhase";
797+ installPhase = "ln -s $src $out";
798+ };
799+
800+ "st" = pkgs.stdenv.mkDerivation {
801+ name = "st";
802+ version = "v6.0.0";
803+ src = pkgs.fetchgit {
804+ url = "https://github.com/purescript/purescript-st.git";
805+ rev = "2cc7ae1c3318a303378c4a9f3fa0f10ee7cc3589";
806+ sha256 = "1ykk27zn03svfsv2s1mf88d6x3q6qhb9bwjzgals79krw8w0aiyl";
807+ };
808+ phases = "installPhase";
809+ installPhase = "ln -s $src $out";
810+ };
811+
812+ "strings" = pkgs.stdenv.mkDerivation {
813+ name = "strings";
814+ version = "v6.0.0";
815+ src = pkgs.fetchgit {
816+ url = "https://github.com/purescript/purescript-strings.git";
817+ rev = "4bc6954448d056f8aa7a659695a6ad60ad4fdf19";
818+ sha256 = "101va3rawcn5inp20krfg192bbjbvm27xfzwwc1inb2ya0m98amg";
819+ };
820+ phases = "installPhase";
821+ installPhase = "ln -s $src $out";
822+ };
823+
824+ "tailrec" = pkgs.stdenv.mkDerivation {
825+ name = "tailrec";
826+ version = "v6.0.0";
827+ src = pkgs.fetchgit {
828+ url = "https://github.com/purescript/purescript-tailrec.git";
829+ rev = "5e2104aa734b31a17074cc805bf087d72b65afd1";
830+ sha256 = "0vc3dpsd2xxz694zlm75nx1cn6c6f29nzkb8xlxkk6ydw3wpsk3w";
831+ };
832+ phases = "installPhase";
833+ installPhase = "ln -s $src $out";
834+ };
835+
836+ "test-unit" = pkgs.stdenv.mkDerivation {
837+ name = "test-unit";
838+ version = "v17.0.0";
839+ src = pkgs.fetchgit {
840+ url = "https://github.com/bodil/purescript-test-unit.git";
841+ rev = "3112d7ebe06d467238058a6384dc75ffd960d335";
842+ sha256 = "02gfb4dqbj5v7dwifm8jhr0lh7lfp5hari5795w1z48wy3apnvvx";
843+ };
844+ phases = "installPhase";
845+ installPhase = "ln -s $src $out";
846+ };
847+
848+ "transformers" = pkgs.stdenv.mkDerivation {
849+ name = "transformers";
850+ version = "v6.0.0";
851+ src = pkgs.fetchgit {
852+ url = "https://github.com/purescript/purescript-transformers.git";
853+ rev = "be72ab52357d9a665cbf93d73ba1c07c4b0957ee";
854+ sha256 = "0ijrdsppl8vx0rlbwc9p0vjbmvwlcy4ia7xymvk6y34zbxzjlzm6";
855+ };
856+ phases = "installPhase";
857+ installPhase = "ln -s $src $out";
858+ };
859+
860+ "tuples" = pkgs.stdenv.mkDerivation {
861+ name = "tuples";
862+ version = "v7.0.0";
863+ src = pkgs.fetchgit {
864+ url = "https://github.com/purescript/purescript-tuples.git";
865+ rev = "4f52da2729b448c8564369378f1232d8d2dc1d8b";
866+ sha256 = "1m1ng0xxicb73945jymcl1hn2y2hmynlnmhb2k0kkn1jrjwgcc3d";
867+ };
868+ phases = "installPhase";
869+ installPhase = "ln -s $src $out";
870+ };
871+
872+ "type-equality" = pkgs.stdenv.mkDerivation {
873+ name = "type-equality";
874+ version = "v4.0.1";
875+ src = pkgs.fetchgit {
876+ url = "https://github.com/purescript/purescript-type-equality.git";
877+ rev = "0525b7d39e0fbd81b4209518139fb8ab02695774";
878+ sha256 = "1ass38jdycsjisdimdc4drg2w8vkkwp6lkvz3kvy7q0h98vdmlbr";
879+ };
880+ phases = "installPhase";
881+ installPhase = "ln -s $src $out";
882+ };
883+
884+ "typelevel-prelude" = pkgs.stdenv.mkDerivation {
885+ name = "typelevel-prelude";
886+ version = "v7.0.0";
887+ src = pkgs.fetchgit {
888+ url = "https://github.com/purescript/purescript-typelevel-prelude.git";
889+ rev = "dca2fe3c8cfd5527d4fe70c4bedfda30148405bf";
890+ sha256 = "0x86mrg33kpnrnsfp4p3c92j5lpyqzy87bxdynwf7smk3inqr2jc";
891+ };
892+ phases = "installPhase";
893+ installPhase = "ln -s $src $out";
894+ };
895+
896+ "uint" = pkgs.stdenv.mkDerivation {
897+ name = "uint";
898+ version = "v7.0.0";
899+ src = pkgs.fetchgit {
900+ url = "https://github.com/purescript-contrib/purescript-uint.git";
901+ rev = "9e4f76ffd5192472f75583844172fe8ab3c0cb9f";
902+ sha256 = "173bhrd006q53s7agwyasxhfbr89x9jpz5b47vm2fr74l3jcw3lq";
903+ };
904+ phases = "installPhase";
905+ installPhase = "ln -s $src $out";
906+ };
907+
908+ "undefined-is-not-a-problem" = pkgs.stdenv.mkDerivation {
909+ name = "undefined-is-not-a-problem";
910+ version = "v1.1.0";
911+ src = pkgs.fetchgit {
912+ url = "https://github.com/paluh/purescript-undefined-is-not-a-problem.git";
913+ rev = "8ac77ac8525343f7319cd48bf2ceb736286f3b27";
914+ sha256 = "1qv7gdyslfj86ga0a7gxkbpvykm3s1x4kzcm8zi75lhlvkpyjh3w";
915+ };
916+ phases = "installPhase";
917+ installPhase = "ln -s $src $out";
918+ };
919+
920+ "unfoldable" = pkgs.stdenv.mkDerivation {
921+ name = "unfoldable";
922+ version = "v6.0.0";
923+ src = pkgs.fetchgit {
924+ url = "https://github.com/purescript/purescript-unfoldable.git";
925+ rev = "493dfe04ed590e20d8f69079df2f58486882748d";
926+ sha256 = "15z2k639ph8wdkrc2y838m5am1z7szw2vqymmv021skzisyn7zwf";
927+ };
928+ phases = "installPhase";
929+ installPhase = "ln -s $src $out";
930+ };
931+
932+ "unicode" = pkgs.stdenv.mkDerivation {
933+ name = "unicode";
934+ version = "v6.0.0";
935+ src = pkgs.fetchgit {
936+ url = "https://github.com/purescript-contrib/purescript-unicode.git";
937+ rev = "6454d59d9e1fd1bc5a72e80e985d8282022a567a";
938+ sha256 = "0w2gsmqcy7pj5vd22qzpig20bdgzmbagyqxrg9mm1njjfsmcm8cv";
939+ };
940+ phases = "installPhase";
941+ installPhase = "ln -s $src $out";
942+ };
943+
944+ "unsafe-coerce" = pkgs.stdenv.mkDerivation {
945+ name = "unsafe-coerce";
946+ version = "v6.0.0";
947+ src = pkgs.fetchgit {
948+ url = "https://github.com/purescript/purescript-unsafe-coerce.git";
949+ rev = "ab956f82e66e633f647fb3098e8ddd3ec58d689f";
950+ sha256 = "0r6d3dx8jalfzvrvkagz9v05yxwkkhgbzlpswg4w1cyl03zjcla4";
951+ };
952+ phases = "installPhase";
953+ installPhase = "ln -s $src $out";
954+ };
955+
956+ "untagged-union" = pkgs.stdenv.mkDerivation {
957+ name = "untagged-union";
958+ version = "6c5bccef38696a16e86ba9a65cf778b4462c4a66";
959+ src = pkgs.fetchgit {
960+ url = "https://github.com/rowtype-yoga/purescript-untagged-union.git";
961+ rev = "6c5bccef38696a16e86ba9a65cf778b4462c4a66";
962+ sha256 = "0s4pik6xcr7zqhlrjxjqgzp8hfg09kgd58zq3xjj0inw9ly35ywn";
963+ };
964+ phases = "installPhase";
965+ installPhase = "ln -s $src $out";
966+ };
967+
968+ "variant" = pkgs.stdenv.mkDerivation {
969+ name = "variant";
970+ version = "v8.0.0";
971+ src = pkgs.fetchgit {
972+ url = "https://github.com/natefaubion/purescript-variant.git";
973+ rev = "8b2bd73a4202257d80a955c4dd36f6055f149823";
974+ sha256 = "18nyxnaqqg1zzydwd2hh9hbzr31j509g5yb1lnd0m60bma4l9h5g";
975+ };
976+ phases = "installPhase";
977+ installPhase = "ln -s $src $out";
978+ };
979+
980+ "web-dom" = pkgs.stdenv.mkDerivation {
981+ name = "web-dom";
982+ version = "v6.0.0";
983+ src = pkgs.fetchgit {
984+ url = "https://github.com/purescript-web/purescript-web-dom.git";
985+ rev = "568a1ee158b29e6e739e7a9aaed3e35ca4c4305a";
986+ sha256 = "0angs86ld0h3ld889ag55lh62101dfn3fknyqrfv0imgn40vwm44";
987+ };
988+ phases = "installPhase";
989+ installPhase = "ln -s $src $out";
990+ };
991+
992+ "web-events" = pkgs.stdenv.mkDerivation {
993+ name = "web-events";
994+ version = "v4.0.0";
995+ src = pkgs.fetchgit {
996+ url = "https://github.com/purescript-web/purescript-web-events.git";
997+ rev = "2124356117be7b764a2f3948032255ac4dab7051";
998+ sha256 = "0l47gsgj71x0gjbhjqwsar2y44ljhq3xa0nkgr1qvqxmcm00llqf";
999+ };
1000+ phases = "installPhase";
1001+ installPhase = "ln -s $src $out";
1002+ };
1003+
1004+ "web-file" = pkgs.stdenv.mkDerivation {
1005+ name = "web-file";
1006+ version = "v4.0.0";
1007+ src = pkgs.fetchgit {
1008+ url = "https://github.com/purescript-web/purescript-web-file.git";
1009+ rev = "023786ae62bbb8bf58156dd7f02011fa38221ef1";
1010+ sha256 = "1wjf1m5y2qsba83is297icv9zyl7jilxdga0192mm1rp0j16jjln";
1011+ };
1012+ phases = "installPhase";
1013+ installPhase = "ln -s $src $out";
1014+ };
1015+
1016+ "web-html" = pkgs.stdenv.mkDerivation {
1017+ name = "web-html";
1018+ version = "v4.0.0";
1019+ src = pkgs.fetchgit {
1020+ url = "https://github.com/purescript-web/purescript-web-html.git";
1021+ rev = "be189cf91b9a19cf493637423522e2fe4a0088cc";
1022+ sha256 = "0yspv6sci3mkflvqwf5jvz3a8nclmhbvazx9n34acyj3aai7k6bs";
1023+ };
1024+ phases = "installPhase";
1025+ installPhase = "ln -s $src $out";
1026+ };
1027+
1028+ "web-storage" = pkgs.stdenv.mkDerivation {
1029+ name = "web-storage";
1030+ version = "v5.0.0";
1031+ src = pkgs.fetchgit {
1032+ url = "https://github.com/purescript-web/purescript-web-storage.git";
1033+ rev = "6b74461e136755db70c271dc898d51776363d7e2";
1034+ sha256 = "0q1grh8cp6h1szsqg3xmwlzrc1bbz8yzlwxlcl1iym5mjiyzgkcs";
1035+ };
1036+ phases = "installPhase";
1037+ installPhase = "ln -s $src $out";
1038+ };
1039+
1040+ };
1041+
1042+ cpPackage = pkg:
1043+ let
1044+ target = ".spago/${pkg.name}/${pkg.version}";
1045+ in ''
1046+ if [ ! -e ${target} ]; then
1047+ echo "Installing ${target}."
1048+ mkdir -p ${target}
1049+ cp --no-preserve=mode,ownership,timestamp -r ${toString pkg.outPath}/* ${target}
1050+ else
1051+ echo "${target} already exists. Skipping."
1052+ fi
1053+ '';
1054+
1055+ getGlob = pkg: ''".spago/${pkg.name}/${pkg.version}/src/**/*.purs"'';
1056+
1057+ getStoreGlob = pkg: ''"${pkg.outPath}/src/**/*.purs"'';
1058+
1059+in {
1060+ inherit inputs;
1061+
1062+ installSpagoStyle = pkgs.writeShellScriptBin "install-spago-style" ''
1063+ set -e
1064+ echo installing dependencies...
1065+ ${builtins.toString (builtins.map cpPackage (builtins.attrValues inputs))}
1066+ echo "echo done."
1067+ '';
1068+
1069+ buildSpagoStyle = pkgs.writeShellScriptBin "build-spago-style" ''
1070+ set -e
1071+ echo building project...
1072+ purs compile ${builtins.toString (builtins.map getGlob (builtins.attrValues inputs))} "$@"
1073+ echo done.
1074+ '';
1075+
1076+ buildFromNixStore = pkgs.writeShellScriptBin "build-from-store" ''
1077+ set -e
1078+ echo building project using sources from nix store...
1079+ purs compile ${builtins.toString (
1080+ builtins.map getStoreGlob (builtins.attrValues inputs))} "$@"
1081+ echo done.
1082+ '';
1083+
1084+ mkBuildProjectOutput =
1085+ { src, purs }:
1086+
1087+ pkgs.stdenv.mkDerivation {
1088+ name = "build-project-output";
1089+ src = src;
1090+
1091+ buildInputs = [ purs ];
1092+
1093+ installPhase = ''
1094+ mkdir -p $out
1095+ purs compile "$src/**/*.purs" ${builtins.toString
1096+ (builtins.map
1097+ (x: ''"${x.outPath}/src/**/*.purs"'')
1098+ (builtins.attrValues inputs))}
1099+ mv output $out
1100+ '';
1101+ };
1102+}
spago.dhall+3−1
@@ -29,14 +29,16 @@ to generate this file without the comments in this block.
29 , "exceptions"
29 , "exceptions"
30 , "foldable-traversable"
30 , "foldable-traversable"
31 , "foreign"
31 , "foreign"
32− , "foreign-object"
33 , "functions"
32 , "functions"
33+ , "integers"
34 , "literals"
34 , "literals"
35 , "maybe"
35 , "maybe"
36 , "newtype"
36 , "newtype"
37 , "nullable"
37 , "nullable"
38+ , "numbers"
38 , "orders"
39 , "orders"
39 , "prelude"
40 , "prelude"
41+ , "random"
40 , "refs"
42 , "refs"
41 , "run"
43 , "run"
42 , "strings"
44 , "strings"
tests/src/test.spec.ts+3−2
@@ -85,7 +85,7 @@ async function screenshot(page: Page) {
85 await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "hidden" })
85 await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "hidden" })
86 await screenshot(page)
86 await screenshot(page)
87 } else {
87 } else {
88− await expect.soft(page).toHaveScreenshot({ mask: [page.locator(".hide-in-test")], animations: "disabled", fullPage: true })
88+ await expect.soft(page).toHaveScreenshot({ animations: "disabled", fullPage: true, maxDiffPixelRatio: 0.01 })
89 }
89 }
90}
90}
91
91
@@ -108,7 +108,8 @@ function cipherPage(name: string) {
108}
108}
109
109
110test.beforeEach(async ({ page }) => {
110test.beforeEach(async ({ page }) => {
111− await page.goto(`file://${__dirname}/../../build/all/app/install/index.html`);
111+ await page.goto(`file://${__dirname}/../../dist/index.html`);
112+ await page.addStyleTag({ content: '.hide-in-test {opacity: 0;}' })
112})
113})
113
114
114if (process.env.RECORD_TEST === "true") {
115if (process.env.RECORD_TEST === "true") {
tests/src/test.spec.ts-snapshots/Card-cipher-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Cards-view-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Contact-cipher-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Contacts-view-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Card-entry-page-2-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Contact-entry-page-2-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Note-entry-page-2-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Empty-ciphers-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Filled-ciphers-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-cipher-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-entry-page-2-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Note-cipher-page-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Notes-view-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Passwords-view-1-Pixel-3-linux.png

Binary file not shown.

tests/src/test.spec.ts-snapshots/Side-bar-1-Pixel-3-linux.png

Binary file not shown.

vanilladeleted−1

Submodule changed.

appname.apparmor → warden.apparmorrenamed+1−1
@@ -5,5 +5,5 @@
5 "audio",
5 "audio",
6 "networking"
6 "networking"
7 ],
7 ],
8− "policy_version": 16.04
8+ "policy_version": 20.04
9}
9}
appname.desktop → warden.desktoprenamed

The content is the same.