on 35824db help sign in
94 files changed+1569−5475
since splitsnapshots
unifiedsplit
.github/FUNDING.yml−1 .github/workflows/build.yaml+8−72 .gitignore+1−11 .gitmodules+6 .helix/languages.toml−3 README.md+1−10 warden.apparmor → appname.apparmor+1−1 warden.desktop → appname.desktop backend-main.js−3 backend/BW.js+55−40 backend/BW.purs+38−37 backend/BW/Logic.purs+239−316 backend/BW/Types.purs+390−445 backend/Bridge.purs+1−93 backend/Data/EncString.js+1−1 backend/Data/SymmetricCryptoKey.js+2−1 backend/FFI.js+1−16 backend/Main.purs+131−182 backend/Storage.purs+1−7 bridge.yaml+94−117 build.sh+14−2 clickable.yaml+10−2 deps/bw+1 dev/build-backend.sh+1−7 dev/build-bw.sh−2 dev/build.sh−10 dev/elm-live.sh+1−5 dev/elm2nix.sh−8 dev/generate-bridge.sh+1−1 dev/record-test.sh−11 dev/spago2nix.sh−3 dev/start-chrome.sh+1−7 dev/test.sh−9 dev/update-tests.sh−9 elm-srcs.nix−62 elm.json−1 flake.lock−177 flake.nix−133 frontend.nix−49 frontend/Bridge.elm+4−130 frontend/Logic/SecondFactor.elm−31 frontend/Main.elm+190−280 frontend/Page.elm+15−23 frontend/Pages/Captcha.elm+35−22 frontend/Pages/Cipher.elm+51−62 frontend/Pages/Ciphers.elm+129−140 frontend/Pages/EditCipher.elm+61−61 frontend/Pages/Login.elm+42−38 frontend/Pages/MasterPassword.elm+30−19 frontend/Pages/SecondFactor.elm−91 frontend/Pages/SecondFactorSelect.elm−164 img/donate.png index.html+1−1 manifest.json+6−6 package-lock.json−959 packages.dhall+4−4 registry.dat scratchpad/password_generator.html+1−16 spago-packages.nix−1102 spago.dhall−5 style.css−7 tests/package-lock.json−73 tests/package.json−9 tests/playwright.config.ts−65 tests/src/test.spec.ts−257 tests/src/test.spec.ts-snapshots/Card-cipher-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Cards-view-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Contact-cipher-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Contacts-view-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Create-Card-entry-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Create-Card-entry-page-2-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Create-Contact-entry-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Create-Contact-entry-page-2-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Create-Note-entry-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Create-Note-entry-page-2-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Empty-ciphers-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Filled-ciphers-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Login-cipher-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Login-entry-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Login-entry-page-2-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Login-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Note-cipher-page-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Notes-view-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Passwords-view-1-Pixel-3-linux.png tests/src/test.spec.ts-snapshots/Side-bar-1-Pixel-3-linux.png tests/vaultwarden/.env−3 tests/vaultwarden/README.md−4 tests/vaultwarden/data/db.sqlite3 tests/vaultwarden/data/db.sqlite3-shm tests/vaultwarden/data/db.sqlite3-wal tests/vaultwarden/data/rsa_key.pem−27 tests/vaultwarden/data/rsa_key.pub.pem−9 tests/vaultwarden/run.sh−13 vanilla+1
.github/FUNDING.ymldeleted−1
@@ -1 +0,0 @@
1−custom: ["https://iko.soy/donate"]
.github/workflows/build.yaml+8−72
@@ -2,96 +2,32 @@ name: CI
22
33on:
44 push:
5− branches: ["master"]
5+ branches: [ "master" ]
66 pull_request:
7− branches: ["master"]
8− release:
9− types: [prereleased]
7+ branches: [ "master" ]
108
119jobs:
1210 build:
1311 runs-on: ubuntu-latest
1412
1513 steps:
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 }}'
23−
2414 - uses: actions/checkout@v3
2515 with:
2616 submodules: recursive
2717
2818 - name: Install clickable
2919 run: |
30− docker --version
3120 sudo apt install adb git python3 python3-pip python3-setuptools
32− pip3 install --user clickable-ut==7.10.0 --upgrade
21+ pip3 install --user clickable-ut --upgrade
3322 echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
3423
35− - name: Build artifacts
36− run: ./dev/build.sh
24+ - name: Build bw libraries
25+ run: ./dev/build-bw.sh
3726
3827 - name: Build Warden
39− run: clickable build --output click
40−
41− - uses: actions/upload-artifact@v3
42− with:
43− name: click
44− path: click/*
45−
46− - uses: actions/upload-artifact@v3
47− with:
48− name: dist
49− path: dist
50−
51− # Tests
52−
53− - uses: actions/setup-node@v3
54− with:
55− node-version: 16
56−
57− - name: Install test dependencies
58− working-directory: tests
59− run: |
60− npm ci
61− npx playwright install --with-deps
62−
63− - name: Run tests
64− working-directory: tests
65− run: npx playwright test
28+ run: clickable build --output dist
6629
6730 - uses: actions/upload-artifact@v3
68− if: always()
69− with:
70− name: 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
9331 with:
94− repo_token: ${{ secrets.GITHUB_TOKEN }}
95− file: build/all/app/*.click
96− tag: ${{ github.ref }}
97− file_glob: true
32+ name: warden
33+ path: dist/*
.gitignore+1−11
@@ -7,16 +7,6 @@ static/backend.js
77.spago
88output
99.psc-ide-port
10+backend.js
1011elm.js
1112output-es
12−elm-stuff
13−node_modules/
14−/tests/test-results/
15−/tests/playwright-report/
16−/tests/playwright/.cache/
17−generated-docs
18−backend.js.map
19−.spago2nix
20−backend.js
21−vanilla
22−dist
.gitmodulesnew file+6
@@ -0,0 +1,6 @@
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.tomldeleted−3
@@ -1,3 +0,0 @@
1−[[language]]
2−name = "nix"
3−auto-format = true
README.md+1−10
@@ -5,7 +5,7 @@
55<h1 align="center">Warden</h1>
66
77<p align="center">
8− <a href="https://open-store.io/app/soy.iko.warden"><img src="https://open-store.io/badges/en_US.svg" alt="OpenStore" style="width: 150px;" /></a>
8+ <a href="https://open-store.io/app/soy.iko.warden"><img src="https://open-store.io/badges/en_US.svg" alt="OpenStore" style="width: 100px;" /></a>
99</p>
1010
1111
@@ -24,15 +24,6 @@ Warden is a [Bitwarden](https://bitwarden.com)/[Vaultwarden](https://github.com/
24241. View and edit: passwords, contact, secure notes and credit cards
25252. Generate passwords
2626
27−## Support the project
28−
29−I fund the development of Warden out of my own pocket. If you find the project useful and don't want it to die, please consider donating what you think the app is worth to you.
30−
31−<p align="center">
32− <a href="https://iko.soy/donate"><img src="img/donate.png" alt="Donate" style="width: 150px;" /></a>
33−</p>
34−
35−
3627## Porting
3728
3829Although Warden is intended for ubuntu touch it is written completely in web technologies. This means that, if there is demand, the app can be ported to virtually any platform. The biggest problem will be [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
warden.apparmor → appname.apparmorrenamed+1−1
@@ -5,5 +5,5 @@
55 "audio",
66 "networking"
77 ],
8− "policy_version": 20.04
8+ "policy_version": 16.04
99}
warden.desktop → appname.desktoprenamed

The content is the same.

backend-main.jsdeleted−3
@@ -1,3 +0,0 @@
1−import * as Main from "./backend/Main/index.js"
2−
3−Main.main()
backend/BW.js+55−40
@@ -1,29 +1,27 @@
1−import { StateFactory } from "../../bw/factories/stateFactory";
2−import { GlobalState } from "../../bw/models/domain/global-state";
3−import { ApiService } from "../../bw/services/api.service";
4−import { AppIdService } from "../../bw/services/appId.service";
5−import { ConsoleLogService } from "../../bw/services/consoleLog.service";
6−import { EnvironmentService } from "../../bw/services/environment.service";
7−import { StateMigrationService } from "../../bw/services/stateMigration.service";
8−import { TokenService } from "../../bw/services/token.service";
9−import { WebCryptoFunctionService } from "../../bw/services/webCryptoFunction.service";
10−import { Account, AccountProfile, AccountTokens } from "../../bw/models/domain/account";
11−import {MemoryStorageService} from "../../bw/services/memoryStorage.service"
12−
13−// import BrowserStorageService from "../../../../bw/services/browserStorage.service";
14−import { CryptoService } from "../../bw/services/crypto.service";
15−
16−import { I18nService } from "../../bw/services/i18n.service";
17−import { StateService as BaseStateService } from "../../bw/services/state.service";
18−import { PasswordTokenRequest } from "../../bw/models/request/identity-token/password-token.request";
19−import { TokenTwoFactorRequest} from "../../bw/models/request/identity-token/token-two-factor.request";
20−import { CipherRequest } from "../../bw/vault/models/request/cipher.request";
21−import { CipherCreateRequest } from "../../bw/vault/models/request/cipher-create.request";
22−import { Cipher } from "../../bw/vault/models/domain/cipher";
23−import { CipherData } from "../../bw/vault/models/data/cipher.data";
24−import { PasswordGenerationService } from "../../bw/services/passwordGeneration.service";
25−import { TotpService } from "../../bw/services/totp.service"
26−import { EncryptServiceImplementation } from "../../bw/services/cryptography/encrypt.service.implementation"
1+import { StateFactory } from "../../deps/bw/libs/shared/dist/src/factories/stateFactory";
2+import { GlobalState } from "../../deps/bw/libs/shared/dist/src/models/domain/globalState";
3+import { ApiService } from "../../deps/bw/libs/shared/dist/src/services/api.service";
4+import { AppIdService } from "../../deps/bw/libs/shared/dist/src/services/appId.service";
5+import { ConsoleLogService } from "../../deps/bw/libs/shared/dist/src/services/consoleLog.service";
6+import { EnvironmentService } from "../../deps/bw/libs/shared/dist/src/services/environment.service";
7+import { StateMigrationService } from "../../deps/bw/libs/shared/dist/src/services/stateMigration.service";
8+import { TokenService } from "../../deps/bw/libs/shared/dist/src/services/token.service";
9+import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/services/webCryptoFunction.service";
10+import { Account, AccountProfile, AccountTokens } from "../../deps/bw/libs/shared/dist/src/models/domain/account";
11+
12+// import BrowserStorageService from "../../../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
13+import { CryptoService } from "../../deps/bw/libs/shared/dist/src/services/crypto.service";
14+
15+import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
16+import { StateService as BaseStateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
17+import { PasswordTokenRequest } from "../../deps/bw/libs/shared/dist/src/models/request/identityToken/passwordTokenRequest";
18+import { TokenRequestTwoFactor } from "../../deps/bw/libs/common/src/models/request/identityToken/tokenRequestTwoFactor";
19+import { CipherRequest } from "../../deps/bw/libs/common/src/models/request/cipherRequest";
20+import { CipherCreateRequest } from "../../deps/bw/libs/common/src/models/request/cipherCreateRequest";
21+import { Cipher } from "../../deps/bw/libs/common/src/models/domain/cipher";
22+import { CipherData } from "../../deps/bw/libs/common/src/models/data/cipherData";
23+import { PasswordGenerationService } from "../../deps/bw/libs/common/src/services/passwordGeneration.service";
24+import { TotpService } from "../../deps/bw/libs/shared/dist/src/services/totp.service"
2725
2826function sanitize(obj) {
2927 return JSON.parse(JSON.stringify(obj));
@@ -38,7 +36,7 @@ export function getServices() {
3836
3937 await bg.bootstrap()
4038
41− await bg.environmentService.setUrls(urls)
39+ bg.environmentService.setUrls(urls)
4240
4341 if (tokenResponse != null) {
4442 const accountInformation = await bg.tokenService.decodeToken(tokenResponse.accessToken);
@@ -75,11 +73,7 @@ export function getServices() {
7573 req.email,
7674 req.masterPasswordHash,
7775 req.captchaResponse,
78− new TokenTwoFactorRequest(
79− req.twoFactor.provider,
80− req.twoFactor.token,
81− req.twoFactor.remember
82− ),
76+ new TokenRequestTwoFactor(req.twoFactor),
8377 req.device
8478 )
8579 ),
@@ -87,7 +81,6 @@ export function getServices() {
8781 postCipherCreate: (cipher) => api.postCipherCreate(makeCipherCreateRequest(cipher)),
8882 putCipher: (cipher) => api.putCipher(cipher.id, makeCipherRequest(cipher)),
8983 deleteCipher: (id) => api.deleteCipher(id),
90− postTwoFactorEmail: (req) => api.postTwoFactorEmail(req),
9184 }
9285 },
9386 crypto: bg.cryptoService,
@@ -133,7 +126,6 @@ class MainBackground {
133126 new StateFactory(GlobalState, Account)
134127 );
135128 this.stateService = new StateService(
136− this.storageService,
137129 this.storageService,
138130 this.storageService,
139131 this.logService,
@@ -164,13 +156,8 @@ class MainBackground {
164156 );
165157 this.i18nService = new I18nService("en");
166158 this.cryptoFunctionService = new WebCryptoFunctionService(window);
167− this.encryptService = new EncryptServiceImplementation(
168− this.cryptoFunctionService,
169− this.logService
170− );
171159 this.cryptoService = new BrowserCryptoService(
172160 this.cryptoFunctionService,
173− this.encryptService,
174161 this.platformUtilsService,
175162 this.logService,
176163 this.stateService
@@ -214,6 +201,34 @@ class MainBackground {
214201 }
215202}
216203
204+class MemoryStorageService {
205+
206+ constructor() {
207+ this.storage = {};
208+ }
209+
210+ async get(key) {
211+ return this.storage[key]
212+ }
213+
214+ async has(key) {
215+ return key in this.storage;
216+ }
217+
218+ async save(key, obj) {
219+ if (obj == null) {
220+ this.remove(key)
221+ return
222+ }
223+
224+ this.storage[key] = obj
225+ }
226+
227+ async remove(key) {
228+ delete this.storage[key]
229+ }
230+}
231+
217232class BrowserCryptoService extends CryptoService {
218233 async retrieveKeyFromStorage(keySuffix) {
219234 if (keySuffix === "biometric") {
@@ -294,7 +309,7 @@ class BrowserPlatformUtilsService {
294309 saveFile(win, blobData, blobOptions, fileName) {
295310 // BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
296311 }
297− getApplicationVersionNumber() {
312+ getApplicationVersion() {
298313 // return Promise.resolve(BrowserApi.getApplicationVersion());
299314 }
300315 supportsWebAuthn(win) {
backend/BW.purs+38−37
@@ -17,31 +17,30 @@ import Effect (Effect)
1717import Effect.Unsafe (unsafePerformEffect)
1818import Untagged.Union (type (|+|))
1919
20−type TotpService =
21− { getCode :: String -> Promise String
22− , getTimeInterval :: String -> Int
23− }
24−
25−type CryptoService =
26− { makeKey :: Fn4 Password String KDF KdfConfig (Promise SymmetricCryptoKey)
27− , hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
28− , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
29− , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
30− , encrypt :: Fn2 String SymmetricCryptoKey (Promise EncString)
31− , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
32− }
33−
34−type ApiService =
35− { postPrelogin :: PreloginRequest -> Promise PreloginResponse
36− , getProfile :: Unit -> Promise ProfileResponse
37− -- TODO: There can really be more request and response types. Maybe handle this later.
38− , postIdentityToken :: PasswordTokenRequest -> Promise (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
39− , getSync :: Unit -> Promise SyncResponse
40− , postCipherCreate :: CipherResponse -> Promise CipherResponse
41− , putCipher :: CipherResponse -> Promise CipherResponse
42− , deleteCipher :: String -> Promise Unit
43− , postTwoFactorEmail :: TwoFactorEmailRequest -> Promise Unit
44− }
20+type TotpService
21+ = { getCode :: String -> Promise String
22+ , getTimeInterval :: String -> Int
23+ }
24+
25+type CryptoService
26+ = { makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey)
27+ , hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
28+ , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
29+ , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
30+ , encrypt :: Fn2 String SymmetricCryptoKey (Promise EncString)
31+ , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
32+ }
33+
34+type ApiService
35+ = { postPrelogin :: PreloginRequest -> Promise PreloginResponse
36+ , getProfile :: Unit -> Promise ProfileResponse
37+ -- TODO: There can really be more request and response types. Maybe handle this later.
38+ , postIdentityToken :: PasswordTokenRequest -> Promise (IdentityCaptchaResponse |+| IdentityTokenResponse)
39+ , getSync :: Unit -> Promise SyncResponse
40+ , postCipherCreate :: CipherResponse -> Promise CipherResponse
41+ , putCipher :: CipherResponse -> Promise CipherResponse
42+ , deleteCipher :: String -> Promise Unit
43+ }
4544
4645type PasswordGeneration =
4746 { generatePassword :: Bridge.PasswordGeneratorConfig -> Promise String
@@ -55,13 +54,15 @@ cryptoFunctionsTypeSha512 = "sha512" :: CryptoFunctionsType
5554
5655cryptoFunctionsTypeMd5 = "md5" :: CryptoFunctionsType
5756
58−type CryptoFunctionsType = String
57+type CryptoFunctionsType
58+ = String
5959
60−type CryptoFunctions =
61− { hash :: Fn2 String CryptoFunctionsType (Promise Hash)
62− }
60+type CryptoFunctions
61+ = { hash :: Fn2 String CryptoFunctionsType (Promise Hash)
62+ }
6363
64−newtype Hash = Hash (ArrayBuffer)
64+newtype Hash
65+ = Hash (ArrayBuffer)
6566
6667hashToArray :: Hash -> Array Int
6768hashToArray (Hash buf) =
@@ -84,12 +85,12 @@ instance EncodeJson Hash where
8485instance DecodeJson Hash where
8586 decodeJson = decodeJson >>> map arrayToHash
8687
87−type Services =
88− { crypto :: CryptoService
89− , getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
90− , cryptoFunctions :: CryptoFunctions
91− , passwordGeneration :: PasswordGeneration
92− , totpService :: TotpService
93− }
88+type Services
89+ = { crypto :: CryptoService
90+ , getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
91+ , cryptoFunctions :: CryptoFunctions
92+ , passwordGeneration :: PasswordGeneration
93+ , totpService :: TotpService
94+ }
9495
9596foreign import getServices :: Effect Services
backend/BW/Logic.purs+239−316
@@ -3,7 +3,6 @@ module BW.Logic where
33import BW
44import BW.Types
55import Prelude
6−
76import Bridge as Bridge
87import Control.Monad.Error.Class (throwError)
98import Control.Promise (Promise)
@@ -13,9 +12,8 @@ import Data.Function.Uncurried (runFn2, runFn3, runFn4)
1312import Data.JNullable (JNullable, fromJNullable, jnull, nullify)
1413import Data.JNullable as JNullable
1514import Data.JOpt (JOpt(..), fromJOpt)
16−import Data.Maybe (Maybe(..), fromMaybe, maybe)
15+import Data.Maybe (Maybe(..), fromMaybe)
1716import Data.Newtype (class Newtype, unwrap, wrap)
18−import Data.Number.Format as Number
1917import Data.String as String
2018import Data.SymmetricCryptoKey (SymmetricCryptoKey)
2119import Data.SymmetricCryptoKey as SymmetricCryptoKey
@@ -27,51 +25,43 @@ import Effect.Aff.Class (class MonadAff, liftAff)
2725import Effect.Class (liftEffect)
2826import Effect.Exception (error)
2927import Effect.Exception as Exc
30−import Effect.Random (random)
31−import Localstorage as Storage
3228import Run (Run)
3329import Run.Reader (Reader, askAt)
34−import Storage (DeviceIDKey(..))
3530import Type.Prelude (Proxy(..))
3631import Untagged.Union (type (|+|))
37−import Web.Storage.Storage (Storage)
3832
3933-- | Creates the master key
40−makePreloginKey
41− :: forall r
42− . PreloginResponse
43− -> Email
44− -> Password
45− -> Run
46− ( crypto :: Reader CryptoService
47− , effect :: Effect
48− , aff :: Aff
49− | r
50− )
51− SymmetricCryptoKey
52−makePreloginKey { kdf, kdfIterations, kdfMemory, kdfParallelism } (Email email') password = do
34+makePreloginKey ::
35+ forall r.
36+ PreloginResponse ->
37+ Email ->
38+ Password ->
39+ Run
40+ ( crypto ::
41+ Reader CryptoService
42+ , effect :: Effect
43+ , aff :: Aff
44+ | r
45+ )
46+ SymmetricCryptoKey
47+makePreloginKey { kdf, kdfIterations } (Email email') password = do
5348 crypto <- askAt (Proxy :: _ "crypto")
5449 let
5550 email = (String.trim >>> String.toLower) email'
56− liftPromise $ runFn4 crypto.makeKey password email kdf
57− { iterations: kdfIterations
58− , memory: kdfMemory
59− , parallelism: kdfParallelism
60− }
51+ liftPromise $ runFn4 crypto.makeKey password email kdf kdfIterations
6152
62−makeDecryptionKey
63− :: forall r
64− .
53+makeDecryptionKey ::
54+ forall r.
6555 -- | Matser key (prelogin key)
66− SymmetricCryptoKey
67− -> EncryptedString
68− -> Run
69− ( crypto :: Reader CryptoService
70− , effect :: Effect
71− , aff :: Aff
72− | r
73− )
74− SymmetricCryptoKey
56+ SymmetricCryptoKey ->
57+ EncryptedString ->
58+ Run
59+ ( crypto :: Reader CryptoService
60+ , effect :: Effect
61+ , aff :: Aff
62+ | r
63+ )
64+ SymmetricCryptoKey
7565makeDecryptionKey masterKey str = do
7666 crypto <- askAt (Proxy :: _ "crypto")
7767 let
@@ -88,145 +78,107 @@ makeDecryptionKey masterKey str = do
8878 liftEffect $ Exc.throw $ "Unsupported key encryption type: " <> show encType
8979 pure $ SymmetricCryptoKey.fromArrayBuffer key
9080
91−getLogInRequestToken
92− :: forall r
93− . { prelogin :: PreloginResponse
94− , email :: Email
95− , password :: Password
96− , captchaResponse :: Maybe String
97− , secondFactor :: Bridge.Cmd_Login_secondFactor_Maybe
98− }
99− -> Run
100− ( api :: Reader ApiService
101− , crypto :: Reader CryptoService
102− , effect :: Effect
103− , aff :: Aff
104− , storage :: Reader Storage
105− | r
106− )
107− (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
108−getLogInRequestToken
109− { prelogin
110− , email
111− , password
112− , captchaResponse
113− , secondFactor: Bridge.Cmd_Login_secondFactor_Maybe secondFactor
114− } = do
81+getLogInRequestToken ::
82+ forall r.
83+ PreloginResponse ->
84+ Email ->
85+ Password ->
86+ Maybe String ->
87+ Run
88+ ( api :: Reader ApiService
89+ , crypto :: Reader CryptoService
90+ , effect :: Effect
91+ , aff :: Aff
92+ | r
93+ )
94+ (IdentityCaptchaResponse |+| IdentityTokenResponse)
95+getLogInRequestToken prelogin email password captchaResponse = do
11596 key <- makePreloginKey prelogin email password
11697 crypto <- askAt (Proxy :: _ "crypto")
117− let
118− twoFactor =
119− maybe
120− { provider: twoFactorProviderTypeEmail
121− , token: ""
122− , remember: false
123− }
124− ( \(Bridge.Cmd_Login_secondFactor x@{ provider }) ->
125− x { provider = bridgeToSecondFactorType provider }
126− )
127− secondFactor
98+ api :: ApiService <- askAt (Proxy :: _ "api")
12899 _localHashedPassword <-
129100 liftPromise
130101 $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
131102 StringHash hashedPassword <- liftPromise $ runFn3 crypto.hashPassword password key jnull
132− deviceId <- getDeviceID
133− api :: ApiService <- askAt (Proxy :: _ "api")
134103 liftPromise
135104 $ api.postIdentityToken
136105 { email: email
137106 , masterPasswordHash: hashedPassword
138107 , captchaResponse: fromMaybe "" captchaResponse
139− , twoFactor
108+ , twoFactor:
109+ { provider: twoFactorProviderTypeEmail
110+ , token: ""
111+ , remember: false
112+ }
140113 , device:
141114 { type: deviceTypeUnknownBrowser
142− , name: "Ubuntu Touch"
143− , identifier: deviceId
115+ , name: "Temporary device name"
116+ , identifier: "42"
144117 , pushToken: jnull
145118 }
146119 }
147120
148−bwPasswordStringHash
149− :: forall r
150− . PreloginResponse
151− -> Email
152− -> Password
153− -> Run
154− ( crypto :: Reader CryptoService
155− , effect :: Effect
156− , aff :: Aff
157− | r
158− )
159− StringHash
160−bwPasswordStringHash prelogin email password = do
161− key <- makePreloginKey prelogin email password
162− crypto <- askAt (Proxy :: _ "crypto")
163− _localHashedPassword <-
164− liftPromise
165− $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
166− liftPromise $ runFn3 crypto.hashPassword password key jnull
167−
168−hashPassword
169− :: forall r
170− . Password
171− -> Run
172− ( cryptoFunctions :: Reader CryptoFunctions
173− , aff :: Aff
174− , effect :: Effect
175− | r
176− )
177− Hash
121+hashPassword ::
122+ forall r.
123+ Password ->
124+ Run
125+ ( cryptoFunctions :: Reader CryptoFunctions
126+ , aff :: Aff
127+ , effect :: Effect
128+ | r
129+ )
130+ Hash
178131hashPassword (Password password) = do
179132 cryptoFunctions <- askAt (Proxy :: _ "cryptoFunctions")
180133 liftPromise $ runFn2 cryptoFunctions.hash password cryptoFunctionsTypeSha512
181134
182−decrypt
183− :: forall r
184− . EncryptedString
185− -> Run
186− ( key :: Reader SymmetricCryptoKey
187− , crypto :: Reader CryptoService
188− , aff :: Aff
189− , effect :: Effect
190− | r
191− )
192− String
135+decrypt ::
136+ forall r.
137+ EncryptedString ->
138+ Run
139+ ( key :: Reader SymmetricCryptoKey
140+ , crypto :: Reader CryptoService
141+ , aff :: Aff
142+ , effect :: Effect
143+ | r
144+ )
145+ String
193146decrypt input = do
194147 crypto <- askAt (Proxy :: _ "crypto")
195148 key <- askAt (Proxy :: _ "key")
196149 liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
197150
198−encrypt
199− :: forall r
200− . String
201− -> Run
202− ( key :: Reader SymmetricCryptoKey
203− , crypto :: Reader CryptoService
204− , aff :: Aff
205− , effect :: Effect
206− | r
207− )
208− EncryptedString
151+encrypt ::
152+ forall r.
153+ String ->
154+ Run
155+ ( key :: Reader SymmetricCryptoKey
156+ , crypto :: Reader CryptoService
157+ , aff :: Aff
158+ , effect :: Effect
159+ | r
160+ )
161+ EncryptedString
209162encrypt input = do
210163 crypto <- askAt (Proxy :: _ "crypto")
211164 key <- askAt (Proxy :: _ "key")
212165 map EncString.toString $ liftPromise $ runFn2 crypto.encrypt input key
213166
214−liftPromise :: forall m a. MonadAff m => Promise a -> m a
167+liftPromise ∷ forall m a. MonadAff m ⇒ Promise a → m a
215168liftPromise = liftAff <<< Promise.toAff
216169
217−encodeCipher
218− :: forall r
219− . Bridge.FullCipher
220− -> Run
221− ( key :: Reader SymmetricCryptoKey
222− , crypto :: Reader CryptoService
223− , aff :: Aff
224− , effect :: Effect
225− | r
226− )
227− CipherResponse
228−encodeCipher
229− (Bridge.FullCipher { name, cipher, id, favorite, reprompt, collectionIds }) = do
170+encodeCipher ::
171+ forall r.
172+ Bridge.FullCipher ->
173+ Run
174+ ( key :: Reader SymmetricCryptoKey
175+ , crypto :: Reader CryptoService
176+ , aff :: Aff
177+ , effect :: Effect
178+ | r
179+ )
180+ CipherResponse
181+encodeCipher (Bridge.FullCipher { name, cipher, id, favorite, reprompt }) = do
230182 let
231183 f :: CipherResponse -> Run _ CipherResponse
232184 f x = case cipher of
@@ -240,14 +192,14 @@ encodeCipher
240192 pure
241193 x
242194 { card =
243− nullify
244− { cardholderName
245− , brand
246− , number
247− , expMonth
248− , expYear
249− , code
250− }
195+ nullify
196+ { cardholderName
197+ , brand
198+ , number
199+ , expMonth
200+ , expYear
201+ , code
202+ }
251203 , type = cipherTypeCard
252204 }
253205 Bridge.IdentityCipher (Bridge.Cipher_IdentityCipher identity) -> do
@@ -272,26 +224,26 @@ encodeCipher
272224 pure
273225 x
274226 { identity =
275− nullify
276− { address1
277− , address2
278− , address3
279− , city
280− , company
281− , country
282− , email
283− , firstName
284− , lastName
285− , licenseNumber
286− , middleName
287− , passportNumber
288− , phone
289− , postalCode
290− , ssn
291− , state
292− , title
293− , username
294− }
227+ nullify
228+ { address1
229+ , address2
230+ , address3
231+ , city
232+ , company
233+ , country
234+ , email
235+ , firstName
236+ , lastName
237+ , licenseNumber
238+ , middleName
239+ , passportNumber
240+ , phone
241+ , postalCode
242+ , ssn
243+ , state
244+ , title
245+ , username
246+ }
295247 , type = cipherTypeIdentity
296248 }
297249 Bridge.LoginCipher (Bridge.Cipher_LoginCipher login) -> do
@@ -299,7 +251,7 @@ encodeCipher
299251 uris <-
300252 traverse
301253 ( \uri -> do
302− uriEnc <- nullify <$> encrypt uri
254+ uriEnc <- encrypt uri
303255 pure { uri: uriEnc, match: jnull }
304256 )
305257 (unwrap login.uris)
@@ -308,14 +260,14 @@ encodeCipher
308260 pure
309261 x
310262 { login =
311− nullify
312− { password
313− , uris: JOpt $ opt uris
314− , username
315− , passwordRevisionDate: jnull
316− , totp: totp
317− , autofillOnPageLoad: JOpt undefined
318− }
263+ nullify
264+ { password
265+ , uris: JOpt $ opt uris
266+ , username
267+ , passwordRevisionDate: jnull
268+ , totp: totp
269+ , autofillOnPageLoad: JOpt undefined
270+ }
319271 , type = cipherTypeLogin
320272 }
321273 Bridge.NoteCipher note -> do
@@ -345,168 +297,139 @@ encodeCipher
345297 , revisionDate: jnull
346298 , attachments: jnull
347299 , passwordHistory: jnull
348− , collectionIds: nullify $ unwrap collectionIds
300+ , collectionIds: jnull
349301 , deletedDate: jnull
350302 , reprompt
351303 }
352304
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
361− :: forall r
362− . CipherResponse
363− -> Run
364− ( key :: Reader SymmetricCryptoKey
365− , crypto :: Reader CryptoService
366− , aff :: Aff
367− , effect :: Effect
368− | r
369− )
370− (Maybe Bridge.FullCipher)
371−decodeCipher cipher | not (supportedCipher cipher) =
372− pure $ Nothing
305+decodeCipher ::
306+ forall r.
307+ CipherResponse ->
308+ Run
309+ ( key :: Reader SymmetricCryptoKey
310+ , crypto :: Reader CryptoService
311+ , aff :: Aff
312+ , effect :: Effect
313+ | r
314+ )
315+ Bridge.FullCipher
373316decodeCipher cipher = do
374317 name <- decrypt cipher.name
375318 cipherType <- case cipher.type of
376319 n
377320 | cipherTypeSecureNote == n -> do
378− note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
379− pure $ Bridge.NoteCipher note
321+ note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
322+ pure $ Bridge.NoteCipher note
380323 n
381324 | cipherTypeLogin == n -> do
382− case JNullable.toMaybe cipher.login of
383− Nothing -> liftEffect $ throwError $ error "Login data is missing"
384− Just login -> do
385− username <- decryptNullable login.username
386− password <- decryptNullable login.password
387− totp <- decryptNullable login.totp
388− uris <- fromJOpt [] <$>
389− ((traverse >>> traverse) (_.uri >>> JNullable.toMaybe >>> maybe (pure "") decrypt) login.uris)
390− pure $ Bridge.LoginCipher
391− $ Bridge.Cipher_LoginCipher
392− { username
393− , password
394− , uris: wrap uris
395− , totp
396− }
325+ case JNullable.toMaybe cipher.login of
326+ Nothing -> liftEffect $ throwError $ error "Login data is missing"
327+ Just login -> do
328+ username <- decryptNullable login.username
329+ password <- decryptNullable login.password
330+ totp <- decryptNullable login.totp
331+ uris <- fromJOpt [] <$> ((traverse >>> traverse) (_.uri >>> decrypt) login.uris)
332+ pure $ Bridge.LoginCipher
333+ $ Bridge.Cipher_LoginCipher
334+ { username
335+ , password
336+ , uris: wrap uris
337+ , totp
338+ }
397339 n
398340 | cipherTypeCard == n -> do
399− case JNullable.toMaybe cipher.card of
400− Nothing -> liftEffect $ throwError $ error "Card data is missing"
401− Just card -> do
402− cardholderName <- decryptNullable card.cardholderName
403− number <- decryptNullable card.number
404− code <- decryptNullable card.code
405− brand <- decryptNullable card.brand
406− expMonth <- decryptNullable card.expMonth
407− expYear <- decryptNullable card.expYear
408− pure $ Bridge.CardCipher
409− $ Bridge.Cipher_CardCipher
410− { brand, cardholderName, code, expMonth, expYear, number }
341+ case JNullable.toMaybe cipher.card of
342+ Nothing -> liftEffect $ throwError $ error "Card data is missing"
343+ Just card -> do
344+ cardholderName <- decryptNullable card.cardholderName
345+ number <- decryptNullable card.number
346+ code <- decryptNullable card.code
347+ brand <- decryptNullable card.brand
348+ expMonth <- decryptNullable card.expMonth
349+ expYear <- decryptNullable card.expYear
350+ pure $ Bridge.CardCipher
351+ $ Bridge.Cipher_CardCipher
352+ { brand, cardholderName, code, expMonth, expYear, number }
411353 n
412354 | cipherTypeIdentity == n -> do
413− case JNullable.toMaybe cipher.identity of
414− Nothing -> liftEffect $ throwError $ error "Identity data is missing"
415− Just identity -> do
416− firstName <- decryptNullable identity.firstName
417− middleName <- decryptNullable identity.middleName
418− lastName <- decryptNullable identity.lastName
419− address1 <- decryptNullable identity.address1
420− address2 <- decryptNullable identity.address2
421− address3 <- decryptNullable identity.address3
422− city <- decryptNullable identity.city
423− company <- decryptNullable identity.company
424− country <- decryptNullable identity.country
425− email <- decryptNullable identity.email
426− licenseNumber <- decryptNullable identity.licenseNumber
427− passportNumber <- decryptNullable identity.passportNumber
428− phone <- decryptNullable identity.phone
429− postalCode <- decryptNullable identity.postalCode
430− ssn <- decryptNullable identity.ssn
431− state <- decryptNullable identity.state
432− title <- decryptNullable identity.title
433− username <- decryptNullable identity.username
434− pure $ Bridge.IdentityCipher
435− $ wrap
436− { firstName
437− , middleName
438− , lastName
439− , address1
440− , address2
441− , address3
442− , city
443− , company
444− , country
445− , email
446− , licenseNumber
447− , passportNumber
448− , phone
449− , postalCode
450− , ssn
451− , state
452− , title
453− , username
454− }
355+ case JNullable.toMaybe cipher.identity of
356+ Nothing -> liftEffect $ throwError $ error "Identity data is missing"
357+ Just identity -> do
358+ firstName <- decryptNullable identity.firstName
359+ middleName <- decryptNullable identity.middleName
360+ lastName <- decryptNullable identity.lastName
361+ address1 <- decryptNullable identity.address1
362+ address2 <- decryptNullable identity.address2
363+ address3 <- decryptNullable identity.address3
364+ city <- decryptNullable identity.city
365+ company <- decryptNullable identity.company
366+ country <- decryptNullable identity.country
367+ email <- decryptNullable identity.email
368+ licenseNumber <- decryptNullable identity.licenseNumber
369+ passportNumber <- decryptNullable identity.passportNumber
370+ phone <- decryptNullable identity.phone
371+ postalCode <- decryptNullable identity.postalCode
372+ ssn <- decryptNullable identity.ssn
373+ state <- decryptNullable identity.state
374+ title <- decryptNullable identity.title
375+ username <- decryptNullable identity.username
376+ pure $ Bridge.IdentityCipher
377+ $ wrap
378+ { firstName
379+ , middleName
380+ , lastName
381+ , address1
382+ , address2
383+ , address3
384+ , city
385+ , company
386+ , country
387+ , email
388+ , licenseNumber
389+ , passportNumber
390+ , phone
391+ , postalCode
392+ , ssn
393+ , state
394+ , title
395+ , username
396+ }
455397 n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
456− pure $ Just
398+ pure
457399 $ wrap
458400 { cipher: cipherType
459401 , name
460402 , id: cipher.id
461403 , favorite: cipher.favorite
462404 , reprompt: cipher.reprompt
463− , collectionIds: wrap <<< fromJNullable [] $ cipher.collectionIds
464405 }
465406
466−decryptNullable
467− :: forall x r
468− . Newtype x (Maybe String)
469− => JNullable EncryptedString
470− -> Run
471− ( key :: Reader SymmetricCryptoKey
472− , crypto :: Reader CryptoService
473− , aff :: Aff
474− , effect :: Effect
475− | r
476− )
477− x
407+decryptNullable ::
408+ forall x r.
409+ Newtype x (Maybe String) =>
410+ JNullable EncryptedString ->
411+ Run
412+ ( key :: Reader SymmetricCryptoKey
413+ , crypto :: Reader CryptoService
414+ , aff :: Aff
415+ , effect :: Effect
416+ | r
417+ )
418+ x
478419decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
479420
480−encryptNullable
481− :: forall x r
482− . Newtype x (Maybe String)
483− => x
484− -> Run
485− ( key :: Reader SymmetricCryptoKey
486− , crypto :: Reader CryptoService
487− , aff :: Aff
488− , effect :: Effect
489− | r
490− )
491− (JNullable EncryptedString)
492−encryptNullable x = case unwrap x of
493− Nothing -> pure jnull
494− Just y -> nullify <$> encrypt y
495−
496−getDeviceID ::
497− forall r.
421+encryptNullable ::
422+ forall x r.
423+ Newtype x (Maybe String) =>
424+ x ->
498425 Run
499− ( effect :: Effect
500− , storage :: Reader Storage
426+ ( key :: Reader SymmetricCryptoKey
427+ , crypto :: Reader CryptoService
428+ , aff :: Aff
429+ , effect :: Effect
501430 | r
502431 )
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
432+ (JNullable EncryptedString)
433+encryptNullable x = case unwrap x of
434+ Nothing -> pure jnull
435+ Just y -> nullify <$> encrypt y
backend/BW/Types.purs+390−445
@@ -2,27 +2,14 @@ module BW.Types where
22
33import Prelude
44
5−import Bridge as Bridge
65import Data.Argonaut (class DecodeJson, class EncodeJson)
7−import Data.Either (Either)
8−import Data.Either as Either
9−import Data.Int as Int
106import Data.JNullable (JNullable)
117import Data.JOpt (JOpt)
12−import Data.Maybe (Maybe(..))
13−import Data.Nullable (Nullable)
148import Data.ShowableJson (ShowableJson)
159import Data.Timestamp (Timestamp)
16−import Data.Traversable (for)
17−import Data.Tuple.Nested ((/\))
18−import Foreign as Foreign
19−import Literals.Undefined (Undefined)
20−import Type.Prelude (Proxy(..))
21−import Untagged.TypeCheck (class HasRuntimeType)
22−import Untagged.Union (type (|+|))
23−import Untagged.Union as Union
2410
25−newtype EncryptedString = EncryptedString String
11+newtype EncryptedString
12+ = EncryptedString String
2613
2714derive newtype instance Show EncryptedString
2815derive newtype instance Ord EncryptedString
@@ -31,86 +18,78 @@ derive newtype instance EncodeJson EncryptedString
3118derive newtype instance DecodeJson EncryptedString
3219
3320kdfPBKDF2_SHA256 = 0 :: KDF
34−kdfArgon2id = 1 :: KDF
3521
36−type KDF = Int
37−
38−type PreloginResponse =
39− { kdf :: KDF
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
49− }
50−
51−type PreloginRequest =
52− { email :: Email
53− }
54−
55−type ProfileResponse =
56− { id :: String
57− , name :: JNullable String
58− , email :: String
59− , emailVerified :: Boolean
60− , masterPasswordHint :: JNullable String
61− -- , premiumPersonally :: Boolean
62− -- , premiumFromOrganization :: Boolean
63− , culture :: JNullable String
64− , twoFactorEnabled :: Boolean
65− , key :: EncryptedString
66− , privateKey :: EncryptedString
67− , securityStamp :: JNullable String
68− , forcePasswordReset :: Boolean
69− , usesKeyConnector :: Boolean
70− -- , organizations :: Array ProfileOrganizationResponse
71− , providers :: Array ProfileProviderResponse
72− , providerOrganizations :: Array ProfileProviderOrganizationResponse
73− }
74−
75−type ProfileOrganizationResponse =
76− { id :: String
77− , name :: JNullable String
78− , usePolicies :: JNullable Boolean
79− , useGroups :: JNullable Boolean
80− , useDirectory :: JNullable Boolean
81− , useEvents :: JNullable Boolean
82− , useTotp :: JNullable Boolean
83− , use2fa :: JNullable Boolean
84− , useApi :: JNullable Boolean
85− , useSso :: JNullable Boolean
86− , useKeyConnector :: JNullable Boolean
87− , useResetPassword :: JNullable Boolean
88− , selfHost :: JNullable Boolean
89− , usersGetPremium :: JNullable Boolean
90− , seats :: JNullable Int
91− , maxCollections :: JNullable Int
92− , maxStorageGb :: JNullable Int
93− , key :: JNullable String
94− , hasPublicAndPrivateKeys :: JNullable Boolean
95− , status :: JNullable OrganizationUserStatusType
96− , type :: JNullable OrganizationUserType
97− , enabled :: JNullable Boolean
98− , ssoBound :: JNullable Boolean
99− , identifier :: JNullable String
100− , permissions :: JNullable PermissionsApi
101− , resetPasswordEnrolled :: JNullable Boolean
102− , userId :: JNullable String
103− , providerId :: JNullable String
104− , providerName :: JNullable String
105− , familySponsorshipFriendlyName :: JNullable String
106− , familySponsorshipAvailable :: JNullable Boolean
107− , planProductType :: JNullable ProductType
108− , keyConnectorEnabled :: JNullable Boolean
109− , keyConnectorUrl :: JNullable String
110− , familySponsorshipLastSyncDate :: JNullable Timestamp
111− , familySponsorshipValidUntil :: JNullable Timestamp
112− , familySponsorshipToDelete :: JNullable Boolean
113− }
22+type KDF
23+ = Int
24+
25+type PreloginResponse
26+ = { kdf :: KDF
27+ , kdfIterations :: Int
28+ }
29+
30+type PreloginRequest
31+ = { email :: String
32+ }
33+
34+type ProfileResponse
35+ = { id :: String
36+ , name :: JNullable String
37+ , email :: String
38+ , emailVerified :: Boolean
39+ , masterPasswordHint :: JNullable String
40+ , premiumPersonally :: Boolean
41+ -- , premiumFromOrganization :: Boolean
42+ , culture :: JNullable String
43+ , twoFactorEnabled :: Boolean
44+ , key :: EncryptedString
45+ , privateKey :: EncryptedString
46+ , securityStamp :: JNullable String
47+ , forcePasswordReset :: Boolean
48+ , usesKeyConnector :: Boolean
49+ , organizations :: Array ProfileOrganizationResponse
50+ , providers :: Array ProfileProviderResponse
51+ , providerOrganizations :: Array ProfileProviderOrganizationResponse
52+ }
53+
54+type ProfileOrganizationResponse
55+ = { id :: String
56+ , name :: JNullable String
57+ , usePolicies :: JNullable Boolean
58+ , useGroups :: JNullable Boolean
59+ , useDirectory :: JNullable Boolean
60+ , useEvents :: JNullable Boolean
61+ , useTotp :: JNullable Boolean
62+ , use2fa :: JNullable Boolean
63+ , useApi :: JNullable Boolean
64+ , useSso :: JNullable Boolean
65+ , useKeyConnector :: JNullable Boolean
66+ , useResetPassword :: JNullable Boolean
67+ , selfHost :: JNullable Boolean
68+ , usersGetPremium :: JNullable Boolean
69+ , seats :: JNullable Int
70+ , maxCollections :: JNullable Int
71+ , maxStorageGb :: JNullable Int
72+ , key :: JNullable String
73+ , hasPublicAndPrivateKeys :: JNullable Boolean
74+ , status :: JNullable OrganizationUserStatusType
75+ , type :: JNullable OrganizationUserType
76+ , enabled :: JNullable Boolean
77+ , ssoBound :: JNullable Boolean
78+ , identifier :: JNullable String
79+ , permissions :: JNullable PermissionsApi
80+ , resetPasswordEnrolled :: JNullable Boolean
81+ , userId :: JNullable String
82+ , providerId :: JNullable String
83+ , providerName :: JNullable String
84+ , familySponsorshipFriendlyName :: JNullable String
85+ , familySponsorshipAvailable :: JNullable Boolean
86+ , planProductType :: JNullable ProductType
87+ , keyConnectorEnabled :: JNullable Boolean
88+ , keyConnectorUrl :: JNullable String
89+ , familySponsorshipLastSyncDate :: JNullable Timestamp
90+ , familySponsorshipValidUntil :: JNullable Timestamp
91+ , familySponsorshipToDelete :: JNullable Boolean
92+ }
11493
11594organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
11695
@@ -118,7 +97,8 @@ organizationUserStatusTypeAccepted = 0 :: OrganizationUserStatusType
11897
11998organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
12099
121−type OrganizationUserStatusType = Int
100+type OrganizationUserStatusType
101+ = Int
122102
123103organizationUserTypeOwner = 0 :: OrganizationUserType
124104
@@ -130,26 +110,27 @@ organizationUserTypeManager = 3 :: OrganizationUserType
130110
131111organizationUserTypeCustom = 4 :: OrganizationUserType
132112
133−type OrganizationUserType = Int
134−
135−type PermissionsApi =
136− { accessEventLogs :: Boolean
137− , accessImportExport :: Boolean
138− , accessReports :: Boolean
139− , manageAllCollections :: Boolean
140− , createNewCollections :: Boolean
141− , editAnyCollection :: Boolean
142− , deleteAnyCollection :: Boolean
143− , manageAssignedCollections :: Boolean
144− , editAssignedCollections :: Boolean
145− , deleteAssignedCollections :: Boolean
146− , manageCiphers :: Boolean
147− , manageGroups :: Boolean
148− , manageSso :: Boolean
149− , managePolicies :: Boolean
150− , manageUsers :: Boolean
151− , manageResetPassword :: Boolean
152− }
113+type OrganizationUserType
114+ = Int
115+
116+type PermissionsApi
117+ = { accessEventLogs :: Boolean
118+ , accessImportExport :: Boolean
119+ , accessReports :: Boolean
120+ , manageAllCollections :: Boolean
121+ , createNewCollections :: Boolean
122+ , editAnyCollection :: Boolean
123+ , deleteAnyCollection :: Boolean
124+ , manageAssignedCollections :: Boolean
125+ , editAssignedCollections :: Boolean
126+ , deleteAssignedCollections :: Boolean
127+ , manageCiphers :: Boolean
128+ , manageGroups :: Boolean
129+ , manageSso :: Boolean
130+ , managePolicies :: Boolean
131+ , manageUsers :: Boolean
132+ , manageResetPassword :: Boolean
133+ }
153134
154135productTypeFree = 0 :: ProductType
155136
@@ -159,19 +140,20 @@ productTypeTeams = 2 :: ProductType
159140
160141productTypeEnterprise = 3 :: ProductType
161142
162−type ProductType = Int
163−
164−type ProfileProviderResponse =
165− { id :: String
166− , name :: JNullable String
167− , key :: JNullable String
168− , status :: JNullable ProviderUserStatusType
169− , type :: JNullable ProviderUserType
170− , enabled :: Boolean
171− , permissions :: PermissionsApi
172− , userId :: JNullable String
173− , useEvents :: Boolean
174− }
143+type ProductType
144+ = Int
145+
146+type ProfileProviderResponse
147+ = { id :: String
148+ , name :: JNullable String
149+ , key :: JNullable String
150+ , status :: JNullable ProviderUserStatusType
151+ , type :: JNullable ProviderUserType
152+ , enabled :: Boolean
153+ , permissions :: PermissionsApi
154+ , userId :: JNullable String
155+ , useEvents :: Boolean
156+ }
175157
176158providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
177159
@@ -179,79 +161,81 @@ providerUserStatusTypeAccepted = 1 :: ProviderUserStatusType
179161
180162providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
181163
182−type ProviderUserStatusType = Int
164+type ProviderUserStatusType
165+ = Int
183166
184167providerUserTypeProviderAdmin = 0 :: ProviderUserType
185168
186169providerUserTypeServiceUser = 1 :: ProviderUserType
187170
188−type ProviderUserType = Int
189−
190−type ProfileProviderOrganizationResponse =
191− { id :: String
192− , name :: JNullable String
193− , usePolicies :: Boolean
194− , useGroups :: Boolean
195− , useDirectory :: Boolean
196− , useEvents :: Boolean
197− , useTotp :: Boolean
198− , use2fa :: Boolean
199− , useApi :: Boolean
200− , useSso :: Boolean
201− , useKeyConnector :: Boolean
202− , useResetPassword :: Boolean
203− , selfHost :: Boolean
204− , usersGetPremium :: Boolean
205− , seats :: Int
206− , maxCollections :: Int
207− , maxStorageGb :: JNullable Int
208− , key :: JNullable String
209− , hasPublicAndPrivateKeys :: Boolean
210− , status :: OrganizationUserStatusType
211− , type :: OrganizationUserType
212− , enabled :: Boolean
213− , ssoBound :: Boolean
214− , identifier :: JNullable String
215− , permissions :: PermissionsApi
216− , resetPasswordEnrolled :: Boolean
217− , userId :: JNullable String
218− , providerId :: JNullable String
219− , providerName :: JNullable String
220− , familySponsorshipFriendlyName :: JNullable String
221− , familySponsorshipAvailable :: Boolean
222− , planProductType :: ProductType
223− , keyConnectorEnabled :: Boolean
224− , keyConnectorUrl :: JNullable String
225− , familySponsorshipLastSyncDate :: JNullable Timestamp
226− , familySponsorshipValidUntil :: JNullable Timestamp
227− , familySponsorshipToDelete :: JNullable Boolean
228− }
229−
230−type Urls =
231− { base :: JNullable String
232− , webVault :: JNullable String
233− , api :: JNullable String
234− , identity :: JNullable String
235− , icons :: JNullable String
236− , notifications :: JNullable String
237− , events :: JNullable String
238− , keyConnector :: JNullable String
239− }
240−
241−type PasswordTokenRequest =
242− { email :: Email
243− , masterPasswordHash :: String
244− , captchaResponse :: String
245− , twoFactor :: TokenRequestTwoFactor
246− , device :: DeviceRequest
247− }
248−
249−type DeviceRequest =
250− { type :: DeviceType
251− , name :: String
252− , identifier :: String
253− , pushToken :: JNullable String
254− }
171+type ProviderUserType
172+ = Int
173+
174+type ProfileProviderOrganizationResponse
175+ = { id :: String
176+ , name :: JNullable String
177+ , usePolicies :: Boolean
178+ , useGroups :: Boolean
179+ , useDirectory :: Boolean
180+ , useEvents :: Boolean
181+ , useTotp :: Boolean
182+ , use2fa :: Boolean
183+ , useApi :: Boolean
184+ , useSso :: Boolean
185+ , useKeyConnector :: Boolean
186+ , useResetPassword :: Boolean
187+ , selfHost :: Boolean
188+ , usersGetPremium :: Boolean
189+ , seats :: Int
190+ , maxCollections :: Int
191+ , maxStorageGb :: JNullable Int
192+ , key :: JNullable String
193+ , hasPublicAndPrivateKeys :: Boolean
194+ , status :: OrganizationUserStatusType
195+ , type :: OrganizationUserType
196+ , enabled :: Boolean
197+ , ssoBound :: Boolean
198+ , identifier :: JNullable String
199+ , permissions :: PermissionsApi
200+ , resetPasswordEnrolled :: Boolean
201+ , userId :: JNullable String
202+ , providerId :: JNullable String
203+ , providerName :: JNullable String
204+ , familySponsorshipFriendlyName :: JNullable String
205+ , familySponsorshipAvailable :: Boolean
206+ , planProductType :: ProductType
207+ , keyConnectorEnabled :: Boolean
208+ , keyConnectorUrl :: JNullable String
209+ , familySponsorshipLastSyncDate :: JNullable Timestamp
210+ , familySponsorshipValidUntil :: JNullable Timestamp
211+ , familySponsorshipToDelete :: JNullable Boolean
212+ }
213+
214+type Urls
215+ = { base :: JNullable String
216+ , webVault :: JNullable String
217+ , api :: JNullable String
218+ , identity :: JNullable String
219+ , icons :: JNullable String
220+ , notifications :: JNullable String
221+ , events :: JNullable String
222+ , keyConnector :: JNullable String
223+ }
224+
225+type PasswordTokenRequest
226+ = { email :: Email
227+ , masterPasswordHash :: String
228+ , captchaResponse :: String
229+ , twoFactor :: TokenRequestTwoFactor
230+ , device :: DeviceRequest
231+ }
232+
233+type DeviceRequest
234+ = { type :: DeviceType
235+ , name :: String
236+ , identifier :: String
237+ , pushToken :: JNullable String
238+ }
255239
256240deviceTypeAndroid = 0 :: DeviceType
257241
@@ -295,13 +279,14 @@ deviceTypeVivaldiExtension = 19 :: DeviceType
295279
296280deviceTypeSafariExtension = 20 :: DeviceType
297281
298−type DeviceType = Int
282+type DeviceType
283+ = Int
299284
300−type TokenRequestTwoFactor =
301− { provider :: TwoFactorProviderType
302− , token :: String
303− , remember :: Boolean
304− }
285+type TokenRequestTwoFactor
286+ = { provider :: TwoFactorProviderType
287+ , token :: String
288+ , remember :: Boolean
289+ }
305290
306291twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
307292
@@ -319,51 +304,24 @@ twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
319304
320305twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
321306
322−type TwoFactorProviderType = Int
323−
324−secondFactorTypeToBridge
325− :: TwoFactorProviderType
326− -> Maybe Bridge.TwoFactorProviderType
327−secondFactorTypeToBridge x = case x of
328− n
329− | n == twoFactorProviderTypeAuthenticator -> Just Bridge.Authenticator
330− n
331− | n == twoFactorProviderTypeEmail -> Just Bridge.Email
332− n
333− | n == twoFactorProviderTypeDuo -> Just Bridge.Duo
334− n
335− | n == twoFactorProviderTypeYubikey -> Just Bridge.Yubikey
336− n
337− | n == twoFactorProviderTypeU2f -> Just Bridge.U2f
338− n
339− | n == twoFactorProviderTypeRemember -> Just Bridge.Remember
340− n
341− | n == twoFactorProviderTypeOrganizationDuo -> Just Bridge.OrganizationDuo
342− n
343− | n == twoFactorProviderTypeWebAuthn -> Just Bridge.WebAuthn
344− _ -> Nothing
345−
346−bridgeToSecondFactorType :: Bridge.TwoFactorProviderType -> TwoFactorProviderType
347−bridgeToSecondFactorType Bridge.Authenticator = twoFactorProviderTypeAuthenticator
348−bridgeToSecondFactorType Bridge.Email = twoFactorProviderTypeEmail
349−bridgeToSecondFactorType Bridge.Duo = twoFactorProviderTypeDuo
350−bridgeToSecondFactorType Bridge.Yubikey = twoFactorProviderTypeYubikey
351−bridgeToSecondFactorType Bridge.U2f = twoFactorProviderTypeU2f
352−bridgeToSecondFactorType Bridge.Remember = twoFactorProviderTypeRemember
353−bridgeToSecondFactorType Bridge.OrganizationDuo = twoFactorProviderTypeOrganizationDuo
354−bridgeToSecondFactorType Bridge.WebAuthn = twoFactorProviderTypeWebAuthn
355−
356−newtype Password = Password String
307+type TwoFactorProviderType
308+ = Int
309+
310+newtype Password
311+ = Password String
357312
358313hashPurposeServerAuthorization = 1 :: HashPurpose
359314
360315hashPurposeLocalAuthorization = 2 :: HashPurpose
361316
362−type HashPurpose = Int
317+type HashPurpose
318+ = Int
363319
364−newtype StringHash = StringHash String
320+newtype StringHash
321+ = StringHash String
365322
366−newtype AccessToken = AccessToken String
323+newtype AccessToken
324+ = AccessToken String
367325
368326derive newtype instance Show AccessToken
369327derive newtype instance Eq AccessToken
@@ -371,7 +329,8 @@ derive newtype instance Ord AccessToken
371329derive newtype instance EncodeJson AccessToken
372330derive newtype instance DecodeJson AccessToken
373331
374−newtype RefreshToken = RefreshToken String
332+newtype RefreshToken
333+ = RefreshToken String
375334
376335derive newtype instance Show RefreshToken
377336derive newtype instance Eq RefreshToken
@@ -383,91 +342,75 @@ type IdentityCaptchaResponse =
383342 { siteKey :: String
384343 }
385344
386−type IdentityTokenResponse =
387− { accessToken :: AccessToken
388− , expiresIn :: Int
389− , refreshToken :: RefreshToken
390− , tokenType :: String
391− , resetMasterPassword :: Boolean
392− , privateKey :: EncryptedString
393− , key :: EncryptedString
394− , twoFactorToken :: JNullable String
395− , kdf :: KDF
396− , kdfIterations :: Int
397− , forcePasswordReset :: JNullable Boolean
398− , apiUseKeyConnector :: JNullable Boolean
399− , keyConnectorUrl :: JNullable String
400− }
401−
402−newtype TwoFactorProviderTypes = TwoFactorProviderTypes (Array (Int |+| String))
403−
404−instance HasRuntimeType TwoFactorProviderTypes where
405− hasRuntimeType Proxy = Foreign.isArray
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−
413−type IdentityTwoFactorResponse =
414− { twoFactorProviders :: TwoFactorProviderTypes
415− -- it is a Map type and untaged unions gets confused
416− -- , twoFactorProviders2 :: Object (Object String)
417− , captchaToken :: String |+| Undefined
418− }
419−
420−newtype Email = Email String
421−
422−type SyncResponse =
423− { profile :: ProfileResponse
424− , folders :: Array FolderResponse
425− , collections :: Array CollectionDetailsResponse
426− , ciphers :: Array CipherResponse
427− , domains :: JNullable DomainsResponse
428− , policies :: JNullable (Array PolicyResponse)
429− , sends :: Array SendResponse
430− }
345+type IdentityTokenResponse
346+ = { accessToken :: AccessToken
347+ , expiresIn :: Int
348+ , refreshToken :: RefreshToken
349+ , tokenType :: String
350+ , resetMasterPassword :: Boolean
351+ , privateKey :: EncryptedString
352+ , key :: EncryptedString
353+ , twoFactorToken :: JNullable String
354+ , kdf :: KDF
355+ , kdfIterations :: Int
356+ , forcePasswordReset :: JNullable Boolean
357+ , apiUseKeyConnector :: JNullable Boolean
358+ , keyConnectorUrl :: JNullable String
359+ }
360+
361+newtype Email
362+ = Email String
363+
364+type SyncResponse
365+ = { profile :: ProfileResponse
366+ , folders :: Array FolderResponse
367+ , collections :: Array CollectionDetailsResponse
368+ , ciphers :: Array CipherResponse
369+ , domains :: JNullable DomainsResponse
370+ , policies :: JNullable (Array PolicyResponse)
371+ , sends :: Array SendResponse
372+ }
373+
374+type FolderResponse
375+ = { id :: String
376+ , name :: JNullable String
377+ , revisionDate :: JNullable String
378+ }
379+
380+type CollectionDetailsResponse
381+ = { id :: String
382+ , organizationId :: JNullable String
383+ , name :: JNullable String
384+ , externalId :: JNullable String
385+ , readOnly :: Boolean
386+ }
431387
432−type FolderResponse =
433− { id :: String
434− , name :: JNullable String
435− , revisionDate :: JNullable String
436− }
437−
438−type CollectionDetailsResponse =
439− { id :: String
440− , organizationId :: JNullable String
441− , name :: JNullable String
442− , externalId :: JNullable String
443− , readOnly :: Boolean
444− }
445388
446389type CipherId = String
447390
448−type CipherResponse =
449− { id :: CipherId
450− , organizationId :: JNullable String
451− , folderId :: JNullable String
452− , type :: CipherType
453− , name :: EncryptedString
454− , notes :: JNullable EncryptedString
455− , fields :: JNullable (Array FieldApi)
456− , login :: JNullable LoginApi
457− , card :: JNullable CardApi
458− , identity :: JNullable IdentityApi
459− , secureNote :: JNullable SecureNoteApi
460− , favorite :: Boolean
461− , edit :: JNullable Boolean
462− , viewPassword :: JNullable Boolean
463− , organizationUseTotp :: JNullable Boolean
464− , revisionDate :: JNullable Timestamp
465− , attachments :: JNullable (Array AttachmentResponse)
466− , passwordHistory :: JNullable (Array PasswordHistoryResponse)
467− , collectionIds :: JNullable (Array String)
468− , deletedDate :: JNullable String
469− , reprompt :: CipherRepromptType
470− }
391+type CipherResponse
392+ = { id :: CipherId
393+ , organizationId :: JNullable String
394+ , folderId :: JNullable String
395+ , type :: CipherType
396+ , name :: EncryptedString
397+ , notes :: JNullable EncryptedString
398+ , fields :: JNullable (Array FieldApi)
399+ , login :: JNullable LoginApi
400+ , card :: JNullable CardApi
401+ , identity :: JNullable IdentityApi
402+ , secureNote :: JNullable SecureNoteApi
403+ , favorite :: Boolean
404+ , edit :: JNullable Boolean
405+ , viewPassword :: JNullable Boolean
406+ , organizationUseTotp :: JNullable Boolean
407+ , revisionDate :: JNullable Timestamp
408+ , attachments :: JNullable (Array AttachmentResponse)
409+ , passwordHistory :: JNullable (Array PasswordHistoryResponse)
410+ , collectionIds :: JNullable (Array String)
411+ , deletedDate :: JNullable String
412+ , reprompt :: CipherRepromptType
413+ }
471414
472415cipherTypeLogin = 1 :: CipherType
473416
@@ -477,14 +420,15 @@ cipherTypeCard = 3 :: CipherType
477420
478421cipherTypeIdentity = 4 :: CipherType
479422
480−type CipherType = Int
423+type CipherType
424+ = Int
481425
482−type FieldApi =
483− { name :: JNullable String
484− , value :: JNullable String
485− , type :: FieldType
486− , linkedId :: JNullable LinkedIdType
487− }
426+type FieldApi
427+ = { name :: JNullable String
428+ , value :: JNullable String
429+ , type :: FieldType
430+ , linkedId :: JNullable LinkedIdType
431+ }
488432
489433fieldTypeText = 0 :: FieldType
490434
@@ -494,7 +438,8 @@ fieldTypeBoolean = 2 :: FieldType
494438
495439fieldTypeLinked = 3 :: FieldType
496440
497−type FieldType = Int
441+type FieldType
442+ = Int
498443
499444linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
500445
@@ -550,22 +495,23 @@ linkedIdTypeIdentityLinkedIdLastName = 417 :: LinkedIdType
550495
551496linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
552497
553−type LinkedIdType = Int
498+type LinkedIdType
499+ = Int
554500
555−type LoginApi =
556− { uris :: JOpt (Array LoginUriApi)
557− , username :: JNullable EncryptedString
558− , password :: JNullable EncryptedString
559− , passwordRevisionDate :: JNullable String
560− , totp :: JNullable EncryptedString
561− -- This thing is undefined in some cases for some reson. Just ignoring it
562− , autofillOnPageLoad :: JOpt (JNullable Boolean)
563− }
501+type LoginApi
502+ = { uris :: JOpt (Array LoginUriApi)
503+ , username :: JNullable EncryptedString
504+ , password :: JNullable EncryptedString
505+ , passwordRevisionDate :: JNullable String
506+ , totp :: JNullable EncryptedString
507+ -- This thing is undefined in some cases for some reson. Just ignoring it
508+ , autofillOnPageLoad :: JOpt (JNullable Boolean)
509+ }
564510
565−type LoginUriApi =
566− { uri :: JNullable EncryptedString
567− , match :: JNullable UriMatchType
568− }
511+type LoginUriApi
512+ = { uri :: EncryptedString
513+ , match :: JNullable UriMatchType
514+ }
569515
570516uriMatchTypeDomain = 0 :: UriMatchType
571517
@@ -579,85 +525,87 @@ uriMatchTypeRegularExpression = 4 :: UriMatchType
579525
580526uriMatchTypeNever = 5 :: UriMatchType
581527
582−type UriMatchType = Int
583−
584−type CardApi =
585− { cardholderName :: JNullable EncryptedString
586− , brand :: JNullable EncryptedString
587− , number :: JNullable EncryptedString
588− , expMonth :: JNullable EncryptedString
589− , expYear :: JNullable EncryptedString
590− , code :: JNullable EncryptedString
591− }
592−
593−type IdentityApi =
594− { title :: JNullable EncryptedString
595− , firstName :: JNullable EncryptedString
596− , middleName :: JNullable EncryptedString
597− , lastName :: JNullable EncryptedString
598− , address1 :: JNullable EncryptedString
599− , address2 :: JNullable EncryptedString
600− , address3 :: JNullable EncryptedString
601− , city :: JNullable EncryptedString
602− , state :: JNullable EncryptedString
603− , postalCode :: JNullable EncryptedString
604− , country :: JNullable EncryptedString
605− , company :: JNullable EncryptedString
606− , email :: JNullable EncryptedString
607− , phone :: JNullable EncryptedString
608− , ssn :: JNullable EncryptedString
609− , username :: JNullable EncryptedString
610− , passportNumber :: JNullable EncryptedString
611− , licenseNumber :: JNullable EncryptedString
612− }
528+type UriMatchType
529+ = Int
530+
531+type CardApi
532+ = { cardholderName :: JNullable EncryptedString
533+ , brand :: JNullable EncryptedString
534+ , number :: JNullable EncryptedString
535+ , expMonth :: JNullable EncryptedString
536+ , expYear :: JNullable EncryptedString
537+ , code :: JNullable EncryptedString
538+ }
539+
540+type IdentityApi
541+ = { title :: JNullable EncryptedString
542+ , firstName :: JNullable EncryptedString
543+ , middleName :: JNullable EncryptedString
544+ , lastName :: JNullable EncryptedString
545+ , address1 :: JNullable EncryptedString
546+ , address2 :: JNullable EncryptedString
547+ , address3 :: JNullable EncryptedString
548+ , city :: JNullable EncryptedString
549+ , state :: JNullable EncryptedString
550+ , postalCode :: JNullable EncryptedString
551+ , country :: JNullable EncryptedString
552+ , company :: JNullable EncryptedString
553+ , email :: JNullable EncryptedString
554+ , phone :: JNullable EncryptedString
555+ , ssn :: JNullable EncryptedString
556+ , username :: JNullable EncryptedString
557+ , passportNumber :: JNullable EncryptedString
558+ , licenseNumber :: JNullable EncryptedString
559+ }
613560
614561secureNoteTypeGeneric = 0 :: SecureNoteType
615562
616−type SecureNoteType = Int
563+type SecureNoteType
564+ = Int
617565
618−type SecureNoteApi =
619− {
620− -- type :: SecureNoteType
621− }
566+type SecureNoteApi = {
567+ type :: SecureNoteType
568+}
622569
623−type AttachmentResponse =
624− { id :: String
625− , url :: JNullable String
626− , fileName :: JNullable String
627− , key :: JNullable String
628− , size :: JNullable String
629− , sizeName :: JNullable String
630− }
570+type AttachmentResponse
571+ = { id :: String
572+ , url :: JNullable String
573+ , fileName :: JNullable String
574+ , key :: JNullable String
575+ , size :: JNullable String
576+ , sizeName :: JNullable String
577+ }
631578
632−type PasswordHistoryResponse =
633− { password :: String
634− , lastUsedDate :: JNullable String
635− }
579+type PasswordHistoryResponse
580+ = { password :: String
581+ , lastUsedDate :: JNullable String
582+ }
636583
637584cipherRepromptTypeNone = 0 :: CipherRepromptType
638585
639586cipherRepromptTypePassword = 1 :: CipherRepromptType
640587
641−type CipherRepromptType = Int
588+type CipherRepromptType
589+ = Int
642590
643−type DomainsResponse =
644− { equivalentDomains :: Array (Array String)
645− , globalEquivalentDomains :: Array GlobalDomainResponse
646− }
591+type DomainsResponse
592+ = { equivalentDomains :: Array (Array String)
593+ , globalEquivalentDomains :: Array GlobalDomainResponse
594+ }
647595
648−type GlobalDomainResponse =
649− { type :: Int
650− , domains :: Array String
651− , excluded :: Boolean
652− }
596+type GlobalDomainResponse
597+ = { type :: Int
598+ , domains :: Array String
599+ , excluded :: Boolean
600+ }
653601
654−type PolicyResponse =
655− { id :: String
656− , organizationId :: JNullable String
657− , type :: PolicyType
658− , data :: ShowableJson
659− , enabled :: Boolean
660− }
602+type PolicyResponse
603+ = { id :: String
604+ , organizationId :: JNullable String
605+ , type :: PolicyType
606+ , data :: ShowableJson
607+ , enabled :: Boolean
608+ }
661609
662610policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
663611
@@ -681,46 +629,43 @@ policyTypeMaximumVaultTimeout = 9 :: PolicyType -- Sets the maximum allowed vaul
681629
682630policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
683631
684−type PolicyType = Int
685−
686−type SendResponse =
687− { id :: String
688− , accessId :: JNullable String
689− , type :: SendType
690− , name :: JNullable String
691− , notes :: JNullable String
692− , file :: JNullable SendFileApi
693− , text :: JNullable SendTextApi
694− , key :: JNullable String
695− , maxAccessCount :: JNullable Int
696− , accessCount :: JNullable Int
697− , revisionDate :: JNullable String
698− , expirationDate :: JNullable String
699− , deletionDate :: JNullable String
700− , password :: JNullable String
701− , disable :: Boolean
702− , hideEmail :: Boolean
703− }
632+type PolicyType
633+ = Int
634+
635+type SendResponse
636+ = { id :: String
637+ , accessId :: JNullable String
638+ , type :: SendType
639+ , name :: JNullable String
640+ , notes :: JNullable String
641+ , file :: JNullable SendFileApi
642+ , text :: JNullable SendTextApi
643+ , key :: JNullable String
644+ , maxAccessCount :: JNullable Int
645+ , accessCount :: JNullable Int
646+ , revisionDate :: JNullable String
647+ , expirationDate :: JNullable String
648+ , deletionDate :: JNullable String
649+ , password :: JNullable String
650+ , disable :: Boolean
651+ , hideEmail :: Boolean
652+ }
704653
705654sendTypeText = 0 :: SendType
706655
707656sendTypeFile = 1 :: SendType
708657
709−type SendType = Int
710−
711−type SendFileApi =
712− { id :: String
713− , fileName :: JNullable String
714− , size :: JNullable String
715− , sizeName :: JNullable String
716− }
658+type SendType
659+ = Int
717660
718−type SendTextApi =
719− { text :: JNullable String
720− , hidden :: Boolean
721− }
661+type SendFileApi
662+ = { id :: String
663+ , fileName :: JNullable String
664+ , size :: JNullable String
665+ , sizeName :: JNullable String
666+ }
722667
723−type TwoFactorEmailRequest =
724− { email :: Email
725− , masterPasswordHash :: StringHash
726− }
668+type SendTextApi
669+ = { text :: JNullable String
670+ , hidden :: Boolean
671+ }
backend/Bridge.purs+1−93
@@ -443,57 +443,10 @@ derive instance genericCipherType :: Generic CipherType _
443443derive instance eqCipherType :: Eq CipherType
444444derive instance ordCipherType :: Ord CipherType
445445
446−newtype Cmd_ChooseSecondFactor =
447− Cmd_ChooseSecondFactor {
448− email :: String
449− , factor :: TwoFactorProviderType
450− , password :: String
451− , requestFromServer :: Boolean
452− , server :: String
453− }
454−
455−derive instance newtypeCmd_ChooseSecondFactor :: Newtype Cmd_ChooseSecondFactor _
456−instance encodeJsonCmd_ChooseSecondFactor :: EncodeJson Cmd_ChooseSecondFactor where
457− encodeJson = genericEncodeAeson Argonaut.defaultOptions
458−instance decodeJsonCmd_ChooseSecondFactor :: DecodeJson Cmd_ChooseSecondFactor where
459− decodeJson = genericDecodeAeson Argonaut.defaultOptions
460−derive instance genericCmd_ChooseSecondFactor :: Generic Cmd_ChooseSecondFactor _
461−derive instance eqCmd_ChooseSecondFactor :: Eq Cmd_ChooseSecondFactor
462−derive instance ordCmd_ChooseSecondFactor :: Ord Cmd_ChooseSecondFactor
463−
464−newtype Cmd_Login_secondFactor =
465− Cmd_Login_secondFactor {
466− provider :: TwoFactorProviderType
467− , remember :: Boolean
468− , token :: String
469− }
470−
471−derive instance newtypeCmd_Login_secondFactor :: Newtype Cmd_Login_secondFactor _
472−instance encodeJsonCmd_Login_secondFactor :: EncodeJson Cmd_Login_secondFactor where
473− encodeJson = genericEncodeAeson Argonaut.defaultOptions
474−instance decodeJsonCmd_Login_secondFactor :: DecodeJson Cmd_Login_secondFactor where
475− decodeJson = genericDecodeAeson Argonaut.defaultOptions
476−derive instance genericCmd_Login_secondFactor :: Generic Cmd_Login_secondFactor _
477−derive instance eqCmd_Login_secondFactor :: Eq Cmd_Login_secondFactor
478−derive instance ordCmd_Login_secondFactor :: Ord Cmd_Login_secondFactor
479−
480−newtype Cmd_Login_secondFactor_Maybe =
481− Cmd_Login_secondFactor_Maybe (Maybe Cmd_Login_secondFactor)
482−
483−derive instance newtypeCmd_Login_secondFactor_Maybe :: Newtype Cmd_Login_secondFactor_Maybe _
484−instance encodeJsonCmd_Login_secondFactor_Maybe :: EncodeJson Cmd_Login_secondFactor_Maybe where
485− encodeJson = genericEncodeAeson Argonaut.defaultOptions
486−instance decodeJsonCmd_Login_secondFactor_Maybe :: DecodeJson Cmd_Login_secondFactor_Maybe where
487− decodeJson = genericDecodeAeson Argonaut.defaultOptions
488−derive instance genericCmd_Login_secondFactor_Maybe :: Generic Cmd_Login_secondFactor_Maybe _
489−derive instance eqCmd_Login_secondFactor_Maybe :: Eq Cmd_Login_secondFactor_Maybe
490−derive instance ordCmd_Login_secondFactor_Maybe :: Ord Cmd_Login_secondFactor_Maybe
491−
492446newtype Cmd_Login =
493447 Cmd_Login {
494448 email :: String
495449 , password :: String
496− , secondFactor :: Cmd_Login_secondFactor_Maybe
497450 , server :: String
498451 }
499452
@@ -507,8 +460,7 @@ derive instance eqCmd_Login :: Eq Cmd_Login
507460derive instance ordCmd_Login :: Ord Cmd_Login
508461
509462data Cmd =
510− ChooseSecondFactor Cmd_ChooseSecondFactor
511− | Copy String
463+ Copy String
512464 | CreateCipher FullCipher
513465 | DeleteCipher FullCipher
514466 | GeneratePassword PasswordGeneratorConfig
@@ -531,22 +483,9 @@ derive instance genericCmd :: Generic Cmd _
531483derive instance eqCmd :: Eq Cmd
532484derive instance ordCmd :: Ord Cmd
533485
534−newtype FullCipher_collectionIds_List =
535− FullCipher_collectionIds_List (Array String)
536−
537−derive instance newtypeFullCipher_collectionIds_List :: Newtype FullCipher_collectionIds_List _
538−instance encodeJsonFullCipher_collectionIds_List :: EncodeJson FullCipher_collectionIds_List where
539− encodeJson = genericEncodeAeson Argonaut.defaultOptions
540−instance decodeJsonFullCipher_collectionIds_List :: DecodeJson FullCipher_collectionIds_List where
541− decodeJson = genericDecodeAeson Argonaut.defaultOptions
542−derive instance genericFullCipher_collectionIds_List :: Generic FullCipher_collectionIds_List _
543−derive instance eqFullCipher_collectionIds_List :: Eq FullCipher_collectionIds_List
544−derive instance ordFullCipher_collectionIds_List :: Ord FullCipher_collectionIds_List
545−
546486newtype FullCipher =
547487 FullCipher {
548488 cipher :: Cipher
549− , collectionIds :: FullCipher_collectionIds_List
550489 , favorite :: Boolean
551490 , id :: String
552491 , name :: String
@@ -634,18 +573,6 @@ derive instance genericSub_NeedsMasterPassword :: Generic Sub_NeedsMasterPasswor
634573derive instance eqSub_NeedsMasterPassword :: Eq Sub_NeedsMasterPassword
635574derive instance ordSub_NeedsMasterPassword :: Ord Sub_NeedsMasterPassword
636575
637−newtype Sub_NeedsSecondFactor_List =
638− Sub_NeedsSecondFactor_List (Array TwoFactorProviderType)
639−
640−derive instance newtypeSub_NeedsSecondFactor_List :: Newtype Sub_NeedsSecondFactor_List _
641−instance encodeJsonSub_NeedsSecondFactor_List :: EncodeJson Sub_NeedsSecondFactor_List where
642− encodeJson = genericEncodeAeson Argonaut.defaultOptions
643−instance decodeJsonSub_NeedsSecondFactor_List :: DecodeJson Sub_NeedsSecondFactor_List where
644− decodeJson = genericDecodeAeson Argonaut.defaultOptions
645−derive instance genericSub_NeedsSecondFactor_List :: Generic Sub_NeedsSecondFactor_List _
646−derive instance eqSub_NeedsSecondFactor_List :: Eq Sub_NeedsSecondFactor_List
647−derive instance ordSub_NeedsSecondFactor_List :: Ord Sub_NeedsSecondFactor_List
648−
649576newtype Sub_Totp =
650577 Sub_Totp {
651578 code :: String
@@ -674,7 +601,6 @@ data Sub =
674601 | NeedsCaptcha String
675602 | NeedsLogin
676603 | NeedsMasterPassword Sub_NeedsMasterPassword
677− | NeedsSecondFactor Sub_NeedsSecondFactor_List
678604 | RecieveEmail String
679605 | Reset
680606 | Totp Sub_Totp
@@ -688,21 +614,3 @@ derive instance genericSub :: Generic Sub _
688614derive instance eqSub :: Eq Sub
689615derive instance ordSub :: Ord Sub
690616
691−data TwoFactorProviderType =
692− Authenticator
693− | Duo
694− | Email
695− | OrganizationDuo
696− | Remember
697− | U2f
698− | WebAuthn
699− | Yubikey
700−
701−instance encodeJsonTwoFactorProviderType :: EncodeJson TwoFactorProviderType where
702− encodeJson = genericEncodeAeson Argonaut.defaultOptions
703−instance decodeJsonTwoFactorProviderType :: DecodeJson TwoFactorProviderType where
704− decodeJson = genericDecodeAeson Argonaut.defaultOptions
705−derive instance genericTwoFactorProviderType :: Generic TwoFactorProviderType _
706−derive instance eqTwoFactorProviderType :: Eq TwoFactorProviderType
707−derive instance ordTwoFactorProviderType :: Ord TwoFactorProviderType
708−
backend/Data/EncString.js+1−1
@@ -1,4 +1,4 @@
1−import { EncString } from "../../bw/models/domain/enc-string"
1+import { EncString } from "../../deps/bw/libs/shared/dist/src/models/domain/encString"
22
33export function fromString(str) {
44 const x = new EncString(str)
backend/Data/SymmetricCryptoKey.js+2−1
@@ -1,5 +1,6 @@
1−import { SymmetricCryptoKey } from '../../bw/models/domain/symmetric-crypto-key.js'
1+import { SymmetricCryptoKey } from '../../deps/bw/libs/shared/dist/src/models/domain/symmetricCryptoKey.js';
22
33export function fromArrayBuffer(arr) {
4+ console.log("fromArrayBuffer " + arr)
45 return new SymmetricCryptoKey(arr)
56}
backend/FFI.js+1−16
@@ -1,21 +1,6 @@
1−function searchForInit(obj) {
2− if (obj.init) {
3− return obj.init
4− } else {
5− for (let key in obj) {
6− if (obj.hasOwnProperty(key)) {
7− const result = searchForInit(obj[key])
8− if (result) {
9− return result
10− }
11− }
12− }
13− }
14−}
15−
161export function startElm(element) {
172 return function () {
18− return searchForInit(Elm)({
3+ return Elm.Main.init({
194 node: document.getElementById(element)
205 })
216 }
backend/Main.purs+131−182
@@ -3,20 +3,18 @@ module Main
33 ) where
44
55import Prelude
6−
76import BW (ApiService, CryptoFunctions, Services, CryptoService)
87import BW as WB
9−import BW.Logic (bwPasswordStringHash, decodeCipher, decrypt, encodeCipher, hashPassword, liftPromise, supportedCipher)
8+import BW.Logic (decodeCipher, decrypt, encodeCipher, hashPassword, liftPromise)
109import BW.Logic as Logic
11−import BW.Types (CipherResponse, Email(..), Password(..), Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote, fromTwoFactorProviderType)
10+import BW.Types (CipherResponse, Email(..), Password(..), Urls, SyncResponse, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
1211import Bridge as Bridge
13−import Control.Monad.Error.Class (catchError)
12+import Control.Monad.Error.Class (catchError, throwError)
1413import Data.Argonaut (class DecodeJson)
1514import Data.Array as Array
1615import Data.Clipboard as Clipboard
1716import Data.DateTime (DateTime)
1817import Data.Either (Either(..))
19−import Data.Foldable (for_)
2018import Data.HCaptcha (bindHCaptchToken)
2119import Data.JNullable (jnull, nullify)
2220import Data.JNullable as JNullable
@@ -30,7 +28,7 @@ import Effect (Effect)
3028import Effect.Aff (Aff, runAff_, try)
3129import Effect.Class (liftEffect)
3230import Effect.Class.Console (log)
33−import Effect.Exception (throw)
31+import Effect.Exception (error)
3432import Effect.Exception as Exc
3533import Effect.Ref as Ref
3634import Elm as Elm
@@ -62,19 +60,19 @@ main = do
6260 let
6361 runAff act = runElmAff app $ run $ act
6462
65− run
66− :: forall a
67− . Run
68− ( app :: Reader Elm
69− , aff :: Aff
70− , effect :: Effect
71− , crypto :: Reader CryptoService
72− , storage :: Reader Storage
73− , services :: Reader Services
74− , cryptoFunctions :: Reader CryptoFunctions
75− )
76− a
77− -> Run (EFFECT + AFF + ()) a
63+ run ::
64+ forall a.
65+ Run
66+ ( app :: Reader Elm
67+ , aff :: Aff
68+ , effect :: Effect
69+ , crypto :: Reader CryptoService
70+ , storage :: Reader Storage
71+ , services :: Reader Services
72+ , cryptoFunctions :: Reader CryptoFunctions
73+ )
74+ a ->
75+ Run (EFFECT + AFF + ()) a
7876 run act =
7977 runReaderAt (Proxy :: _ "app") app
8078 $ runReaderAt (Proxy :: _ "crypto") services.crypto
@@ -83,19 +81,19 @@ main = do
8381 $ runReaderAt (Proxy :: _ "cryptoFunctions") services.cryptoFunctions
8482 $ act
8583
86− runWithDecryptionKey
87− :: Run
88− ( app :: Reader Elm
89− , aff :: Aff
90− , effect :: Effect
91− , key :: Reader SymmetricCryptoKey
92− , crypto :: Reader CryptoService
93− , storage :: Reader Storage
94− , services :: Reader Services
95− , cryptoFunctions :: Reader CryptoFunctions
96− )
97− Unit
98− -> Effect Unit
84+ runWithDecryptionKey ::
85+ Run
86+ ( app :: Reader Elm
87+ , aff :: Aff
88+ , effect :: Effect
89+ , key :: Reader SymmetricCryptoKey
90+ , crypto :: Reader CryptoService
91+ , storage :: Reader Storage
92+ , services :: Reader Services
93+ , cryptoFunctions :: Reader CryptoFunctions
94+ )
95+ Unit ->
96+ Effect Unit
9997 runWithDecryptionKey act = do
10098 maybeKey <- Ref.read masterKeyRef
10199 runAff
@@ -106,10 +104,8 @@ main = do
106104 runReaderAt (Proxy :: _ "key") key $ act
107105 Nothing -> requestMasterPassword
108106 Elm.subscribe app \cmd -> case cmd of
109− Bridge.Login (Bridge.Cmd_Login { email: email', server, password, secondFactor }) -> do
110− let
111− email = Email email'
112− captchaResponse <- Ref.read hCaptchaTokenRef
107+ Bridge.Login (Bridge.Cmd_Login { email, server, password }) -> do
108+ captchaToken <- Ref.read hCaptchaTokenRef
113109 Ref.write Nothing hCaptchaTokenRef
114110 let
115111 urls = baseUrl server
@@ -123,24 +119,15 @@ main = do
123119 ( do
124120 resp <-
125121 runBaseAff' $ run $ runReaderAt (Proxy :: _ "api") unauthedApi
126− $ Logic.getLogInRequestToken
127− { prelogin
128− , email
129− , password: Password password
130− , captchaResponse
131− , secondFactor
132− }
122+ $ Logic.getLogInRequestToken prelogin (Email email) (Password password) captchaToken
133123 pure $ Right resp
134124 )
135− ( \err -> do
136− log $ show err
137− pure $ Left unit
138− )
139− case map (toEither1 >>> map toEither1) $ loginResponse of
125+ (const $ pure $ Left unit)
126+ case map toEither1 loginResponse of
140127 Left _ -> send Bridge.WrongPassword
141128 Right (Left { siteKey }) -> send $ Bridge.NeedsCaptcha siteKey
142− Right (Right (Right token)) -> do
143− masterKey <- Logic.makePreloginKey prelogin email (Password password)
129+ Right (Right token) -> do
130+ masterKey <- Logic.makePreloginKey prelogin (Email email) (Password password)
144131 api <- liftPromise $ services.getApi urls (nullify token)
145132 sync <- liftPromise $ api.getSync unit
146133 hash <- hashPassword (Password password)
@@ -153,16 +140,6 @@ main = do
153140 Storage.store storage SyncKey sync
154141 Storage.store storage TokenKey token
155142 send Bridge.LoginSuccessful
156− Right (Right (Left { twoFactorProviders, captchaToken })) -> do
157− case toEither1 captchaToken of
158− Left captchaTokenString ->
159− liftEffect
160− $ Ref.write (Just captchaTokenString) hCaptchaTokenRef
161− Right _ -> pure unit
162− providers <- case fromTwoFactorProviderType twoFactorProviders of
163− Left x -> liftEffect $ throw $ "Unknown second factor provider: " <> x
164− Right x -> pure x
165− send $ Bridge.NeedsSecondFactor $ Bridge.Sub_NeedsSecondFactor_List providers
166143 Bridge.NeedCiphersList ->
167144 runWithDecryptionKey do
168145 sendCiphers
@@ -186,9 +163,9 @@ main = do
186163 Bridge.Init -> do
187164 Storage.get storage TokenKey
188165 >>= \x ->
189− Elm.send app case x of
190− Just _ -> Bridge.LoginSuccessful
191− Nothing -> Bridge.NeedsLogin
166+ Elm.send app case x of
167+ Just _ -> Bridge.LoginSuccessful
168+ Nothing -> Bridge.NeedsLogin
192169 Bridge.RequestCipher id ->
193170 runWithDecryptionKey do
194171 sync <- getOrReset SyncKey
@@ -196,7 +173,8 @@ main = do
196173 Nothing -> pure unit
197174 Just cipherResponse -> do
198175 cipher <- decodeCipher cipherResponse
199− for_ cipher $ send <<< Bridge.LoadCipher
176+ send $ Bridge.LoadCipher cipher
177+ pure unit
200178 pure unit
201179 Bridge.NeedEmail ->
202180 runWithDecryptionKey do
@@ -212,7 +190,7 @@ main = do
212190 api <- getAuthedApi
213191 cipher <- encodeCipher fullCipher
214192 newCipher <- liftPromise (api.putCipher cipher) >>= decodeCipher
215− for_ newCipher $ send <<< Bridge.CipherChanged
193+ send $ Bridge.CipherChanged newCipher
216194 performSync
217195 pure unit
218196 Bridge.GeneratePassword cfg ->
@@ -225,7 +203,7 @@ main = do
225203 api <- getAuthedApi
226204 cipher <- encodeCipher fullCipher
227205 newCipher <- liftPromise (api.postCipherCreate cipher) >>= decodeCipher
228− for_ newCipher $ send <<< Bridge.CipherChanged
206+ send $ Bridge.CipherChanged newCipher
229207 performSync
230208 pure unit
231209 Bridge.DeleteCipher c@(Bridge.FullCipher { id }) ->
@@ -243,48 +221,21 @@ main = do
243221 interval = totpService.getTimeInterval totp
244222 send $ Bridge.Totp
245223 $ Bridge.Sub_Totp
246− { interval
247− , code
248− , source: totp
224+ { interval, code, source: totp
249225 }
250226 pure unit
251− Bridge.ChooseSecondFactor
252− ( Bridge.Cmd_ChooseSecondFactor
253− { email: email'
254− , factor
255− , server
256− , password
257− , requestFromServer
258− }
259− ) ->
260− when requestFromServer
261− let
262− email = Email email'
263− in
264− case factor of
265− Bridge.Email ->
266− runAff do
267− let
268− urls = baseUrl server
269− unauthedApi <- liftPromise $ services.getApi urls jnull
270− prelogin <- liftPromise $ unauthedApi.postPrelogin { email }
271− masterPasswordHash <- bwPasswordStringHash prelogin email (Password password)
272− liftPromise $ unauthedApi.postTwoFactorEmail { email, masterPasswordHash }
273− pure unit
274− _ -> pure unit
275227
276−processCipher
277− :: forall r
278− . CipherResponse
279− -> Run
280− ( crypto :: Reader CryptoService
281− , key :: Reader SymmetricCryptoKey
282− , aff :: Aff
283− , effect :: Effect
284− | r
285− )
286− (Maybe { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime })
287−processCipher cipher | not (supportedCipher cipher) = pure Nothing
228+processCipher ::
229+ forall r.
230+ CipherResponse ->
231+ Run
232+ ( crypto :: Reader CryptoService
233+ , key :: Reader SymmetricCryptoKey
234+ , aff :: Aff
235+ , effect :: Effect
236+ | r
237+ )
238+ { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime }
288239processCipher cipher = do
289240 name <- decrypt cipher.name
290241 cipherType <- case cipher.type of
@@ -296,12 +247,10 @@ processCipher cipher = do
296247 | n == cipherTypeCard -> pure Bridge.CardType
297248 n
298249 | n == cipherTypeIdentity -> pure Bridge.IdentityType
299− n -> liftEffect $ throw $ "Unsupported cipher type: " <> show n
250+ n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
300251 date <- liftEffect $ maybe (pure bottom) (Timestamp.toDateTime) $ JNullable.toMaybe cipher.revisionDate
301252 pure
302− $ Just
303− $
304− { cipher:
253+ $ { cipher:
305254 Bridge.Sub_LoadCiphers
306255 { name: name
307256 , date: maybe "" Timestamp.toLocalDateTimeString $ JNullable.toMaybe cipher.revisionDate
@@ -335,18 +284,18 @@ baseUrl server =
335284 , keyConnector: jnull
336285 }
337286
338−getOrReset
339− :: forall k t r
340− . StorageKey k t
341− => DecodeJson t
342− => k
343− -> Run
344− ( storage :: Reader Storage
345− , app :: Reader Elm
346− , effect :: Effect
347− | r
348− )
349− t
287+getOrReset ::
288+ forall k t r.
289+ StorageKey k t =>
290+ DecodeJson t =>
291+ k ->
292+ Run
293+ ( storage :: Reader Storage
294+ , app :: Reader Elm
295+ , effect :: Effect
296+ | r
297+ )
298+ t
350299getOrReset key = do
351300 storage <- askAt (Proxy :: _ "storage")
352301 app <- askAt (Proxy :: _ "app")
@@ -357,69 +306,69 @@ getOrReset key = do
357306 Exc.throw "Encountered an internal error. Resetting the app."
358307 liftEffect $ try (Storage.get storage key)
359308 >>= \x -> case x of
360− Left err -> do
361− log $ show err
362− handleError
363− Right Nothing -> handleError
364− Right (Just y) -> pure y
309+ Left err -> do
310+ log $ show err
311+ handleError
312+ Right Nothing -> handleError
313+ Right (Just y) -> pure y
365314
366−getAuthedApi
367− :: forall r
368− . Run
369− ( storage :: Reader Storage
370− , app :: Reader Elm
371− , aff :: Aff
372− , effect :: Effect
373− , services :: Reader Services
374− | r
375− )
376− ApiService
315+getAuthedApi ::
316+ forall r.
317+ Run
318+ ( storage :: Reader Storage
319+ , app :: Reader Elm
320+ , aff :: Aff
321+ , effect :: Effect
322+ , services :: Reader Services
323+ | r
324+ )
325+ ApiService
377326getAuthedApi = do
378327 services <- askAt (Proxy :: _ "services")
379328 token <- getOrReset TokenKey
380329 url <- getOrReset UrlsKey
381330 liftPromise $ services.getApi (baseUrl url) (nullify token)
382331
383−requestMasterPassword
384− :: forall r
385− . Run
386− ( storage :: Reader Storage
387− , app :: Reader Elm
388− , effect :: Effect
389− | r
390− )
391− Unit
332+requestMasterPassword ::
333+ forall r.
334+ Run
335+ ( storage :: Reader Storage
336+ , app :: Reader Elm
337+ , effect :: Effect
338+ | r
339+ )
340+ Unit
392341requestMasterPassword = do
393342 url <- getOrReset UrlsKey
394343 sync <- getOrReset SyncKey
395344 send $ Bridge.NeedsMasterPassword $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
396345
397−send
398− :: forall r
399− . Bridge.Sub
400− -> Run
401− ( app :: Reader Elm
402− , effect :: Effect
403− | r
404− )
405− Unit
346+send ::
347+ forall r.
348+ Bridge.Sub ->
349+ Run
350+ ( app :: Reader Elm
351+ , effect :: Effect
352+ | r
353+ )
354+ Unit
406355send sub = do
407356 app <- askAt (Proxy :: _ "app")
408357 liftEffect $ Elm.send app sub
409358
410−performSync
411− :: forall r
412− . Run
413− ( storage :: Reader Storage
414− , app :: Reader Elm
415− , aff :: Aff
416− , effect :: Effect
417− , services :: Reader Services
418− , crypto :: Reader CryptoService
419− , key :: Reader SymmetricCryptoKey
420− | r
421− )
422− Unit
359+performSync ::
360+ forall r.
361+ Run
362+ ( storage :: Reader Storage
363+ , app :: Reader Elm
364+ , aff :: Aff
365+ , effect :: Effect
366+ , services :: Reader Services
367+ , crypto :: Reader CryptoService
368+ , key :: Reader SymmetricCryptoKey
369+ | r
370+ )
371+ Unit
423372performSync = do
424373 api <- getAuthedApi
425374 storage <- askAt (Proxy :: _ "storage")
@@ -427,22 +376,22 @@ performSync = do
427376 liftEffect $ Storage.store storage SyncKey newSync
428377 sendCiphers
429378
430−sendCiphers
431− :: forall r
432− . Run
433− ( storage :: Reader Storage
434− , app :: Reader Elm
435− , aff :: Aff
436− , effect :: Effect
437− , services :: Reader Services
438− , crypto :: Reader CryptoService
439− , key :: Reader SymmetricCryptoKey
440− | r
441− )
442− Unit
379+sendCiphers ::
380+ forall r.
381+ Run
382+ ( storage :: Reader Storage
383+ , app :: Reader Elm
384+ , aff :: Aff
385+ , effect :: Effect
386+ , services :: Reader Services
387+ , crypto :: Reader CryptoService
388+ , key :: Reader SymmetricCryptoKey
389+ | r
390+ )
391+ Unit
443392sendCiphers = do
444393 sync <- getOrReset SyncKey
445− ciphers <- Array.catMaybes <$> traverse processCipher sync.ciphers
394+ ciphers <- traverse processCipher sync.ciphers
446395 let
447396 sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
448397 send $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
backend/Storage.purs+1−7
@@ -2,7 +2,7 @@ module Storage where
22
33import Prelude
44
5−import BW (Hash)
5+import BW (Hash(..))
66import BW.Types (IdentityTokenResponse, SyncResponse, PreloginResponse)
77import Localstorage (class StorageKey)
88
@@ -35,9 +35,3 @@ data MasterPasswordHashKey
3535
3636instance StorageKey MasterPasswordHashKey Hash where
3737 storageKey MasterPasswordHashKey = "master-password-hash"
38−
39−data DeviceIDKey
40− = DeviceIDKey
41−
42−instance StorageKey DeviceIDKey String where
43− storageKey DeviceIDKey = "device-uuid"
bridge.yaml+94−117
@@ -1,159 +1,136 @@
11Sub:
2− - NeedsLogin
3− - NeedsCaptcha: String
4− - NeedsSecondFactor: { List: TwoFactorProviderType }
5− - CaptchaDone
6− - Error: String
7− - LoadCiphers:
8− List:
2+- NeedsLogin
3+- NeedsCaptcha: String
4+- CaptchaDone
5+- Error: String
6+- LoadCiphers:
7+ List:
98 name: String
109 date: String
1110 id: String
1211 cipherType: CipherType
13− - LoginSuccessful
14− - WrongPassword
15− - NeedsMasterPassword:
16− server: String
17− login: String
18− - Reset
19− - LoadCipher: FullCipher
20− - RecieveEmail: String
21− - CipherChanged: FullCipher
22− - GeneratedPassword: String
23− - CipherDeleted: FullCipher
24− - Totp:
25− source: String
26− code: String
27− interval: Int
12+- LoginSuccessful
13+- WrongPassword
14+- NeedsMasterPassword:
15+ server: String
16+ login: String
17+- Reset
18+- LoadCipher: FullCipher
19+- RecieveEmail: String
20+- CipherChanged: FullCipher
21+- GeneratedPassword: String
22+- CipherDeleted: FullCipher
23+- Totp:
24+ source: String
25+ code: String
26+ interval: Int
2827
2928Cmd:
30− - Init
31− - Login:
32− email: String
33− server: String
34− password: String
35− secondFactor:
36− Maybe:
37− provider: TwoFactorProviderType
38− token: String
39− remember: Bool
40− - ChooseSecondFactor:
41− factor: TwoFactorProviderType
42− email: String
43− server: String
44− password: String
45− requestFromServer: Bool
46− - NeedCiphersList
47− - SendMasterPassword: String
48− - NeedsReset
49− - RequestCipher: String
50− - NeedEmail
51− - Copy: String
52− - Open: String
53− - UpdateCipher: FullCipher
54− - CreateCipher: FullCipher
55− - GeneratePassword: PasswordGeneratorConfig
56− - DeleteCipher: FullCipher
57− - RequestTotp: String
29+- Init
30+- Login:
31+ email: String
32+ server: String
33+ password: String
34+- NeedCiphersList
35+- SendMasterPassword: String
36+- NeedsReset
37+- RequestCipher: String
38+- NeedEmail
39+- Copy: String
40+- Open: String
41+- UpdateCipher: FullCipher
42+- CreateCipher: FullCipher
43+- GeneratePassword: PasswordGeneratorConfig
44+- DeleteCipher: FullCipher
45+- RequestTotp: String
5846
5947FullCipher:
60− reprompt: Int
61− favorite: Bool
62− id: String
63− name: String
64− cipher: Cipher
65− collectionIds: { List: String }
48+ reprompt: Int
49+ favorite: Bool
50+ id: String
51+ name: String
52+ cipher: Cipher
6653
6754Cipher:
68− - LoginCipher:
69− uris: { List: String }
70− username:
55+- LoginCipher:
56+ uris: {List: String}
57+ username:
7158 Maybe: String
72− password:
59+ password:
7360 Maybe: String
74− totp:
61+ totp:
7562 Maybe: String
76− - NoteCipher: String
77− - CardCipher:
78− cardholderName:
63+- NoteCipher: String
64+- CardCipher:
65+ cardholderName:
7966 Maybe: String
80− brand:
67+ brand:
8168 Maybe: String
82− number:
69+ number:
8370 Maybe: String
84− expMonth:
71+ expMonth:
8572 Maybe: String
86− expYear:
73+ expYear:
8774 Maybe: String
88− code:
75+ code:
8976 Maybe: String
90− - IdentityCipher:
91− title:
77+- IdentityCipher:
78+ title:
9279 Maybe: String
93− firstName:
80+ firstName:
9481 Maybe: String
95− middleName:
82+ middleName:
9683 Maybe: String
97− lastName:
84+ lastName:
9885 Maybe: String
99− address1:
86+ address1:
10087 Maybe: String
101− address2:
88+ address2:
10289 Maybe: String
103− address3:
90+ address3:
10491 Maybe: String
105− city:
92+ city:
10693 Maybe: String
107− state:
94+ state:
10895 Maybe: String
109− postalCode:
96+ postalCode:
11097 Maybe: String
111− country:
98+ country:
11299 Maybe: String
113− company:
100+ company:
114101 Maybe: String
115− email:
102+ email:
116103 Maybe: String
117− phone:
104+ phone:
118105 Maybe: String
119− ssn:
106+ ssn:
120107 Maybe: String
121− username:
108+ username:
122109 Maybe: String
123− passportNumber:
110+ passportNumber:
124111 Maybe: String
125− licenseNumber:
112+ licenseNumber:
126113 Maybe: String
127114
128115CipherType:
129− - LoginType
130− - CardType
131− - NoteType
132− - IdentityType
116+- LoginType
117+- CardType
118+- NoteType
119+- IdentityType
133120
134121PasswordGeneratorConfig:
135− length: Int
136− ambiguous: Bool
137− number: Bool
138− minNumber: Int
139− uppercase: Bool
140− minUppercase: Int
141− lowercase: Bool
142− minLowercase: Int
143− special: Bool
144− minSpecial: Int
145− type: String
146− numWords: Int
147− wordSeparator: String
148− capitalize: Bool
149− includeNumber: Bool
150−
151−TwoFactorProviderType:
152− - Authenticator
153− - Email
154− - Duo
155− - Yubikey
156− - U2f
157− - Remember
158− - OrganizationDuo
159− - WebAuthn
122+ length: Int
123+ ambiguous: Bool
124+ number: Bool
125+ minNumber: Int
126+ uppercase: Bool
127+ minUppercase: Int
128+ lowercase: Bool
129+ minLowercase: Int
130+ special: Bool
131+ minSpecial: Int
132+ type: String
133+ numWords: Int
134+ wordSeparator: String
135+ capitalize: Bool
136+ includeNumber: Bool
build.sh+14−2
@@ -1,5 +1,17 @@
1−#!/bin/sh
1+#!/bin/bash
22
33set -e
44
5−cp -r "$SRC_DIR/dist"/* "$INSTALL_DIR"
5+TEMP=$(mktemp -d)
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+10−2
@@ -1,8 +1,16 @@
11clickable_minimum_required: 7.1.2
2−framework: ubuntu-sdk-20.04
32builder: custom
43build: $SRC_DIR/build.sh
54kill: webapp-container*
6−docker_image: clickable/amd64-20.04-amd64
5+image_setup:
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
715
816restrict_arch: all
deps/bwnew file+1

Submodule changed.

dev/build-backend.sh+1−7
@@ -1,9 +1,3 @@
11#!/bin/sh
22
3−set -e
4−
5−spago build
6−
7−export out="$(pwd)/backend.js"
8−
9−$(nix build .#backendBuildScript --print-out-paths) "$(pwd)/output"
3+spago bundle-app --to backend.js
dev/build-bw.sh−2
@@ -1,7 +1,5 @@
11#!/bin/sh
22
3−set -e
4−
53(cd deps/bw && npm install)
64
75(cd deps/bw/libs/common/ && npm run clean && npm exec -c "tsc -m es2020 -t es2018")
dev/build.shdeleted−10
@@ -1,10 +0,0 @@
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+1−5
@@ -1,7 +1,3 @@
11#!/bin/sh
22
3−set -e
4−
5−nix build .#vanilla -o vanilla
6−
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
3+nix run github:NixOS/nixpkgs#elmPackages.elm-live -- frontend/Main.elm --start-page=index.html --proxy-prefix=/api --proxy-host=http://localhost:6009 --pushstate -- --debug --output=elm.js
dev/elm2nix.shdeleted−8
@@ -1,8 +0,0 @@
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/generate-bridge.sh+1−1
@@ -1,3 +1,3 @@
11#!/bin/sh
22
3−nix run github:ilyakooo0/elm-ps-bridge -- --input bridge.yaml --elmOutput=frontend/Bridge.elm --psOutput=backend/Bridge.purs
3+nix run github:ilyakooo0/elm-ps-bridge -- --input bridge.yaml --elmOutput=Frontend/Bridge.elm --psOutput=backend/Bridge.purs
dev/record-test.shdeleted−11
@@ -1,11 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd tests
6−
7−npm install
8−
9−export RECORD_TEST=true
10−
11−npx playwright test
dev/spago2nix.shdeleted−3
@@ -1,3 +0,0 @@
1−#!/bin/sh
2−
3−nix run "github:justinwoo/spago2nix#spago2nix" -- generate
dev/start-chrome.sh+1−7
@@ -1,9 +1,3 @@
11#!/bin/sh
22
3−if test -x "$(which chromium)"; then
4− chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
5−elif test -x /Applications/Chromium.app/Contents/MacOS/Chromium; then
6− /Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
7−else
8− flatpak run com.github.Eloston.UngoogledChromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
9−fi
3+/Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
dev/test.shdeleted−9
@@ -1,9 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd tests
6−
7−npm install
8−
9−npx playwright test
dev/update-tests.shdeleted−9
@@ -1,9 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd tests
6−
7−npm install
8−
9−npx playwright test --update-snapshots
elm-srcs.nixdeleted−62
@@ -1,62 +0,0 @@
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−}
elm.json−1
@@ -13,7 +13,6 @@
1313 "elm/html": "1.0.0",
1414 "elm/json": "1.1.3",
1515 "elm/time": "1.0.0",
16− "mdgriffith/elm-ui": "1.1.8",
1716 "mgold/elm-nonempty-list": "4.2.0"
1817 },
1918 "indirect": {
flake.lockdeleted−177
@@ -1,177 +0,0 @@
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.nixdeleted−133
@@ -1,133 +0,0 @@
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.nixdeleted−49
@@ -1,49 +0,0 @@
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/Bridge.elm+4−130
@@ -491,82 +491,17 @@ jsonEncCipherType val =
491491
492492
493493
494−type alias Cmd_ChooseSecondFactor =
495− { email: String
496− , factor: TwoFactorProviderType
497− , password: String
498− , requestFromServer: Bool
499− , server: String
500− }
501−
502−jsonDecCmd_ChooseSecondFactor : Json.Decode.Decoder ( Cmd_ChooseSecondFactor )
503−jsonDecCmd_ChooseSecondFactor =
504− Json.Decode.succeed (\pemail pfactor ppassword prequestFromServer pserver -> {email = pemail, factor = pfactor, password = ppassword, requestFromServer = prequestFromServer, server = pserver})
505− |> required "email" (Json.Decode.string)
506− |> required "factor" (jsonDecTwoFactorProviderType)
507− |> required "password" (Json.Decode.string)
508− |> required "requestFromServer" (Json.Decode.bool)
509− |> required "server" (Json.Decode.string)
510−
511−jsonEncCmd_ChooseSecondFactor : Cmd_ChooseSecondFactor -> Value
512−jsonEncCmd_ChooseSecondFactor val =
513− Json.Encode.object
514− [ ("email", Json.Encode.string val.email)
515− , ("factor", jsonEncTwoFactorProviderType val.factor)
516− , ("password", Json.Encode.string val.password)
517− , ("requestFromServer", Json.Encode.bool val.requestFromServer)
518− , ("server", Json.Encode.string val.server)
519− ]
520−
521−
522−
523−type alias Cmd_Login_secondFactor =
524− { provider: TwoFactorProviderType
525− , remember: Bool
526− , token: String
527− }
528−
529−jsonDecCmd_Login_secondFactor : Json.Decode.Decoder ( Cmd_Login_secondFactor )
530−jsonDecCmd_Login_secondFactor =
531− Json.Decode.succeed (\pprovider premember ptoken -> {provider = pprovider, remember = premember, token = ptoken})
532− |> required "provider" (jsonDecTwoFactorProviderType)
533− |> required "remember" (Json.Decode.bool)
534− |> required "token" (Json.Decode.string)
535−
536−jsonEncCmd_Login_secondFactor : Cmd_Login_secondFactor -> Value
537−jsonEncCmd_Login_secondFactor val =
538− Json.Encode.object
539− [ ("provider", jsonEncTwoFactorProviderType val.provider)
540− , ("remember", Json.Encode.bool val.remember)
541− , ("token", Json.Encode.string val.token)
542− ]
543−
544−
545−
546−type alias Cmd_Login_secondFactor_Maybe = (Maybe Cmd_Login_secondFactor)
547−
548−jsonDecCmd_Login_secondFactor_Maybe : Json.Decode.Decoder ( Cmd_Login_secondFactor_Maybe )
549−jsonDecCmd_Login_secondFactor_Maybe =
550− Json.Decode.maybe (jsonDecCmd_Login_secondFactor)
551−
552−jsonEncCmd_Login_secondFactor_Maybe : Cmd_Login_secondFactor_Maybe -> Value
553−jsonEncCmd_Login_secondFactor_Maybe val = (maybeEncode (jsonEncCmd_Login_secondFactor)) val
554−
555−
556−
557494type alias Cmd_Login =
558495 { email: String
559496 , password: String
560− , secondFactor: Cmd_Login_secondFactor_Maybe
561497 , server: String
562498 }
563499
564500jsonDecCmd_Login : Json.Decode.Decoder ( Cmd_Login )
565501jsonDecCmd_Login =
566− Json.Decode.succeed (\pemail ppassword psecondFactor pserver -> {email = pemail, password = ppassword, secondFactor = psecondFactor, server = pserver})
502+ Json.Decode.succeed (\pemail ppassword pserver -> {email = pemail, password = ppassword, server = pserver})
567503 |> required "email" (Json.Decode.string)
568504 |> required "password" (Json.Decode.string)
569− |> required "secondFactor" (jsonDecCmd_Login_secondFactor_Maybe)
570505 |> required "server" (Json.Decode.string)
571506
572507jsonEncCmd_Login : Cmd_Login -> Value
@@ -574,15 +509,13 @@ jsonEncCmd_Login val =
574509 Json.Encode.object
575510 [ ("email", Json.Encode.string val.email)
576511 , ("password", Json.Encode.string val.password)
577− , ("secondFactor", jsonEncCmd_Login_secondFactor_Maybe val.secondFactor)
578512 , ("server", Json.Encode.string val.server)
579513 ]
580514
581515
582516
583517type Cmd =
584− ChooseSecondFactor Cmd_ChooseSecondFactor
585− | Copy String
518+ Copy String
586519 | CreateCipher FullCipher
587520 | DeleteCipher FullCipher
588521 | GeneratePassword PasswordGeneratorConfig
@@ -600,8 +533,7 @@ type Cmd =
600533jsonDecCmd : Json.Decode.Decoder ( Cmd )
601534jsonDecCmd =
602535 let jsonDecDictCmd = Dict.fromList
603− [ ("ChooseSecondFactor", Json.Decode.lazy (\_ -> Json.Decode.map ChooseSecondFactor (jsonDecCmd_ChooseSecondFactor)))
604− , ("Copy", Json.Decode.lazy (\_ -> Json.Decode.map Copy (Json.Decode.string)))
536+ [ ("Copy", Json.Decode.lazy (\_ -> Json.Decode.map Copy (Json.Decode.string)))
605537 , ("CreateCipher", Json.Decode.lazy (\_ -> Json.Decode.map CreateCipher (jsonDecFullCipher)))
606538 , ("DeleteCipher", Json.Decode.lazy (\_ -> Json.Decode.map DeleteCipher (jsonDecFullCipher)))
607539 , ("GeneratePassword", Json.Decode.lazy (\_ -> Json.Decode.map GeneratePassword (jsonDecPasswordGeneratorConfig)))
@@ -622,7 +554,6 @@ jsonDecCmd =
622554jsonEncCmd : Cmd -> Value
623555jsonEncCmd val =
624556 let keyval v = case v of
625− ChooseSecondFactor v1 -> ("ChooseSecondFactor", encodeValue (jsonEncCmd_ChooseSecondFactor v1))
626557 Copy v1 -> ("Copy", encodeValue (Json.Encode.string v1))
627558 CreateCipher v1 -> ("CreateCipher", encodeValue (jsonEncFullCipher v1))
628559 DeleteCipher v1 -> ("DeleteCipher", encodeValue (jsonEncFullCipher v1))
@@ -641,20 +572,8 @@ jsonEncCmd val =
641572
642573
643574
644−type alias FullCipher_collectionIds_List = (List String)
645−
646−jsonDecFullCipher_collectionIds_List : Json.Decode.Decoder ( FullCipher_collectionIds_List )
647−jsonDecFullCipher_collectionIds_List =
648− Json.Decode.list (Json.Decode.string)
649−
650−jsonEncFullCipher_collectionIds_List : FullCipher_collectionIds_List -> Value
651−jsonEncFullCipher_collectionIds_List val = (Json.Encode.list Json.Encode.string) val
652−
653−
654−
655575type alias FullCipher =
656576 { cipher: Cipher
657− , collectionIds: FullCipher_collectionIds_List
658577 , favorite: Bool
659578 , id: String
660579 , name: String
@@ -663,9 +582,8 @@ type alias FullCipher =
663582
664583jsonDecFullCipher : Json.Decode.Decoder ( FullCipher )
665584jsonDecFullCipher =
666− Json.Decode.succeed (\pcipher pcollectionIds pfavorite pid pname preprompt -> {cipher = pcipher, collectionIds = pcollectionIds, favorite = pfavorite, id = pid, name = pname, reprompt = preprompt})
585+ Json.Decode.succeed (\pcipher pfavorite pid pname preprompt -> {cipher = pcipher, favorite = pfavorite, id = pid, name = pname, reprompt = preprompt})
667586 |> required "cipher" (jsonDecCipher)
668− |> required "collectionIds" (jsonDecFullCipher_collectionIds_List)
669587 |> required "favorite" (Json.Decode.bool)
670588 |> required "id" (Json.Decode.string)
671589 |> required "name" (Json.Decode.string)
@@ -675,7 +593,6 @@ jsonEncFullCipher : FullCipher -> Value
675593jsonEncFullCipher val =
676594 Json.Encode.object
677595 [ ("cipher", jsonEncCipher val.cipher)
678− , ("collectionIds", jsonEncFullCipher_collectionIds_List val.collectionIds)
679596 , ("favorite", Json.Encode.bool val.favorite)
680597 , ("id", Json.Encode.string val.id)
681598 , ("name", Json.Encode.string val.name)
@@ -800,17 +717,6 @@ jsonEncSub_NeedsMasterPassword val =
800717
801718
802719
803−type alias Sub_NeedsSecondFactor_List = (List TwoFactorProviderType)
804−
805−jsonDecSub_NeedsSecondFactor_List : Json.Decode.Decoder ( Sub_NeedsSecondFactor_List )
806−jsonDecSub_NeedsSecondFactor_List =
807− Json.Decode.list (jsonDecTwoFactorProviderType)
808−
809−jsonEncSub_NeedsSecondFactor_List : Sub_NeedsSecondFactor_List -> Value
810−jsonEncSub_NeedsSecondFactor_List val = (Json.Encode.list jsonEncTwoFactorProviderType) val
811−
812−
813−
814720type alias Sub_Totp =
815721 { code: String
816722 , interval: Int
@@ -846,7 +752,6 @@ type Sub =
846752 | NeedsCaptcha String
847753 | NeedsLogin
848754 | NeedsMasterPassword Sub_NeedsMasterPassword
849− | NeedsSecondFactor Sub_NeedsSecondFactor_List
850755 | RecieveEmail String
851756 | Reset
852757 | Totp Sub_Totp
@@ -866,7 +771,6 @@ jsonDecSub =
866771 , ("NeedsCaptcha", Json.Decode.lazy (\_ -> Json.Decode.map NeedsCaptcha (Json.Decode.string)))
867772 , ("NeedsLogin", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsLogin))
868773 , ("NeedsMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map NeedsMasterPassword (jsonDecSub_NeedsMasterPassword)))
869− , ("NeedsSecondFactor", Json.Decode.lazy (\_ -> Json.Decode.map NeedsSecondFactor (jsonDecSub_NeedsSecondFactor_List)))
870774 , ("RecieveEmail", Json.Decode.lazy (\_ -> Json.Decode.map RecieveEmail (Json.Decode.string)))
871775 , ("Reset", Json.Decode.lazy (\_ -> Json.Decode.succeed Reset))
872776 , ("Totp", Json.Decode.lazy (\_ -> Json.Decode.map Totp (jsonDecSub_Totp)))
@@ -889,39 +793,9 @@ jsonEncSub val =
889793 NeedsCaptcha v1 -> ("NeedsCaptcha", encodeValue (Json.Encode.string v1))
890794 NeedsLogin -> ("NeedsLogin", encodeValue (Json.Encode.list identity []))
891795 NeedsMasterPassword v1 -> ("NeedsMasterPassword", encodeValue (jsonEncSub_NeedsMasterPassword v1))
892− NeedsSecondFactor v1 -> ("NeedsSecondFactor", encodeValue (jsonEncSub_NeedsSecondFactor_List v1))
893796 RecieveEmail v1 -> ("RecieveEmail", encodeValue (Json.Encode.string v1))
894797 Reset -> ("Reset", encodeValue (Json.Encode.list identity []))
895798 Totp v1 -> ("Totp", encodeValue (jsonEncSub_Totp v1))
896799 WrongPassword -> ("WrongPassword", encodeValue (Json.Encode.list identity []))
897800 in encodeSumTaggedObject "tag" "contents" keyval val
898801
899−
900−
901−type TwoFactorProviderType =
902− Authenticator
903− | Duo
904− | Email
905− | OrganizationDuo
906− | Remember
907− | U2f
908− | WebAuthn
909− | Yubikey
910−
911−jsonDecTwoFactorProviderType : Json.Decode.Decoder ( TwoFactorProviderType )
912−jsonDecTwoFactorProviderType =
913− let jsonDecDictTwoFactorProviderType = Dict.fromList [("Authenticator", Authenticator), ("Duo", Duo), ("Email", Email), ("OrganizationDuo", OrganizationDuo), ("Remember", Remember), ("U2f", U2f), ("WebAuthn", WebAuthn), ("Yubikey", Yubikey)]
914− in decodeSumUnaries "TwoFactorProviderType" jsonDecDictTwoFactorProviderType
915−
916−jsonEncTwoFactorProviderType : TwoFactorProviderType -> Value
917−jsonEncTwoFactorProviderType val =
918− case val of
919− Authenticator -> Json.Encode.string "Authenticator"
920− Duo -> Json.Encode.string "Duo"
921− Email -> Json.Encode.string "Email"
922− OrganizationDuo -> Json.Encode.string "OrganizationDuo"
923− Remember -> Json.Encode.string "Remember"
924− U2f -> Json.Encode.string "U2f"
925− WebAuthn -> Json.Encode.string "WebAuthn"
926− Yubikey -> Json.Encode.string "Yubikey"
927−
frontend/Logic/SecondFactor.elmdeleted−31
@@ -1,31 +0,0 @@
1−module Logic.SecondFactor exposing (secondFactorName)
2−
3−import Bridge
4−
5−
6−secondFactorName : Bridge.TwoFactorProviderType -> String
7−secondFactorName factor =
8− case factor of
9− Bridge.Authenticator ->
10− "Authenticator"
11−
12− Bridge.Duo ->
13− "Duo"
14−
15− Bridge.Email ->
16− "Email"
17−
18− Bridge.OrganizationDuo ->
19− "Organization Duo"
20−
21− Bridge.Remember ->
22− "Remember (IDK what this is)"
23−
24− Bridge.U2f ->
25− "U2f"
26−
27− Bridge.WebAuthn ->
28− "WebAuthn"
29−
30− Bridge.Yubikey ->
31− "Yubikey"
frontend/Main.elm+190−280
@@ -1,4 +1,4 @@
1−module Main exposing (main)
1+module Main exposing (..)
22
33import Bridge
44import Browser
@@ -16,8 +16,6 @@ import Pages.Loader exposing (loader)
1616import Pages.Login as Login
1717import Pages.MasterPassword as MasterPassword
1818import Pages.Navigation as Navigation exposing (TopButton(..))
19−import Pages.SecondFactor as SecondFactor
20−import Pages.SecondFactorSelect as SecondFactorSelect
2119import Task
2220import Time
2321import Types exposing (CipherId)
@@ -30,23 +28,31 @@ notificationLingerSeconds =
3028
3129
3230type Msg
33− = ShowLoginPage
31+ = Noop
32+ | RecieveMessage String
33+ | ShowLoginPage
3434 | ShowError String String
3535 | ShowInfo String String
3636 | CloseNotification
37− | SubmitLogin Bridge.Cmd_Login
37+ | SubmitLogin { email : String, password : String, server : String }
38+ | LoginMsg Login.Msg
39+ | CiphersMsg Ciphers.Msg
3840 | LoadCiphers Bridge.Sub_LoadCiphers_List
39− | LoginSuccessful
41+ | OpenCiphersScreen
4042 | Reset
4143 | NeedsReset
44+ | MasterPasswordMsg MasterPassword.Msg
4245 | SendMasterPassword { password : String }
4346 | ShowMasterPasswordPage { server : String, login : String }
47+ | CipherMsg Cipher.Msg
4448 | ShowCipherPage Bridge.FullCipher
4549 | RequestCipher CipherId
4650 | PopView
4751 | RecieveEmail String
4852 | Copy String
4953 | Open String
54+ | CaptchaMsg Captcha.Msg
55+ | EditCipherMsg EditCipher.Msg
5056 | ShowCaptcha Captcha.HCaptchSiteKey
5157 | ClearNotification { currentTime : Time.Posix }
5258 | UpdateLastNotificatioTime Time.Posix
@@ -59,22 +65,6 @@ type Msg
5965 | WrongPassword
6066 | DeleteCipher Bridge.FullCipher
6167 | RequestTotp String
62− | ShowSecondFactorSelect (List Bridge.TwoFactorProviderType)
63− | SelectSecondFactor
64− { provider : Bridge.TwoFactorProviderType
65− , requestFromServer : Bool
66− }
67− | PageMsg PageMsg
68−
69−
70−type PageMsg
71− = LoginMsg Login.Msg
72− | CiphersMsg Ciphers.Msg
73− | MasterPasswordMsg MasterPassword.Msg
74− | CipherMsg Cipher.Msg
75− | EditCipherMsg EditCipher.Msg
76− | SecondFactorSelectMsg SecondFactorSelect.Msg
77− | SecondFactorMsg SecondFactor.Msg
7868
7969
8070type alias Model =
@@ -85,45 +75,19 @@ type alias Model =
8575 }
8676
8777
88−findLoginDetails :
89− List PageModel
90− ->
91− Maybe
92− { server : String
93− , email : String
94− , password : String
95− }
96−findLoginDetails xs =
97− case xs of
98− (LoginModel { server, email, password }) :: _ ->
99− Just
100− { server = server
101− , email = email
102− , password = password
103− }
104−
105− _ :: rest ->
106− findLoginDetails rest
107−
108− [] ->
109− Nothing
110−
111−
11278appendNotification : Notification.Config -> Model -> ( Model, Cmd Msg )
11379appendNotification cfg model =
11480 ( { model | notifications = cfg :: model.notifications }, Task.perform UpdateLastNotificatioTime Time.now )
11581
11682
11783type PageModel
118− = LoginModel (Login.Model Msg)
84+ = LoginModel Login.Model
11985 | LoadingPage
120− | CiphersModel (Ciphers.Model Msg)
86+ | CiphersModel Ciphers.Model
12187 | EditCipherModel (EditCipher.Model Msg)
122− | MasterPasswordModel (MasterPassword.Model Msg)
123− | CipherModel (Cipher.Model Msg)
88+ | MasterPasswordModel MasterPassword.Model
89+ | CipherModel Cipher.Model
12490 | CaptchaModel Captcha.Model
125− | SecondFactorSelectModel (SecondFactorSelect.Model Msg)
126− | SecondFactorModel (SecondFactor.Model Msg)
12791
12892
12993showPage :
@@ -138,43 +102,31 @@ showPage email page =
138102 let
139103 simpleBackButton =
140104 BackButton { action = PopView, icon = Nothing }
141−
142− liftPageWith :
143− (pageMsg -> PageMsg)
144− ->
145− { title : List (Html pageMsg)
146− , body : List (Html pageMsg)
147− , topButton : Maybe (Navigation.TopButton Msg)
148− }
149− ->
150− { title : List (Html Msg)
151− , body : List (Html Msg)
152− , topButton : Maybe (Navigation.TopButton Msg)
153− }
154− liftPageWith lift { title, body, topButton } =
155− { title = title |> List.map (Html.map (lift >> PageMsg))
156− , body = body |> List.map (Html.map (lift >> PageMsg))
157− , topButton = topButton
158− }
159105 in
160106 case page of
161107 LoginModel model ->
162− liftPageWith LoginMsg
163− { title = [ text Login.title ]
164− , body = Login.view model
165− , topButton = Nothing
166− }
108+ let
109+ p =
110+ Login.page loginCallbacks LoginMsg
111+ in
112+ { title = p.title model
113+ , body = p.view model
114+ , topButton = Nothing
115+ }
167116
168117 CiphersModel model ->
169− liftPageWith CiphersMsg
170− { title = Ciphers.title model
171− , body = Ciphers.view model
172− , topButton =
173− Ciphers.menuConfig email model
174− |> Navigation.mapMenuConfig (CiphersMsg >> PageMsg)
175− |> Navigation.MenuButton
176− |> Just
177− }
118+ let
119+ p =
120+ Ciphers.page ciphersCallbacks CiphersMsg
121+ in
122+ { title = p.title model
123+ , body = p.view model
124+ , topButton =
125+ Ciphers.menuConfig email model
126+ |> Navigation.mapMenuConfig CiphersMsg
127+ |> Navigation.MenuButton
128+ |> Just
129+ }
178130
179131 LoadingPage ->
180132 { title = []
@@ -183,45 +135,44 @@ showPage email page =
183135 }
184136
185137 MasterPasswordModel model ->
186− liftPageWith MasterPasswordMsg
187− { title = [ text MasterPassword.title ]
188− , body = MasterPassword.view model
189− , topButton = Nothing
190− }
138+ let
139+ p =
140+ MasterPassword.page masterPasswordCallbacks MasterPasswordMsg
141+ in
142+ { title = p.title model
143+ , body = p.view model
144+ , topButton = Nothing
145+ }
191146
192147 CipherModel model ->
193− liftPageWith CipherMsg
194− { title = Cipher.title model
195− , body = Cipher.view model
196− , topButton = Just simpleBackButton
197− }
148+ let
149+ p =
150+ Cipher.page cipherCallbacks CipherMsg
151+ in
152+ { title = p.title model
153+ , body = p.view model
154+ , topButton = Just simpleBackButton
155+ }
198156
199157 CaptchaModel model ->
200− { title = [ text Captcha.title ]
201− , body = Captcha.view model
158+ let
159+ p =
160+ Captcha.page captchaCallbacks CaptchaMsg
161+ in
162+ { title = p.title model
163+ , body = p.view model
202164 , topButton = Just simpleBackButton
203165 }
204166
205167 EditCipherModel model ->
206− liftPageWith EditCipherMsg
207− { title = EditCipher.title model
208− , body = EditCipher.view model
209− , topButton = Just (BackButton { action = PopView, icon = Just "close" })
210− }
211−
212− SecondFactorSelectModel model ->
213− liftPageWith SecondFactorSelectMsg
214− { title = [ text SecondFactorSelect.title ]
215− , body = SecondFactorSelect.view model
216− , topButton = Just simpleBackButton
217− }
218−
219− SecondFactorModel model ->
220− liftPageWith SecondFactorMsg
221− { title = [ SecondFactor.title model |> text ]
222− , body = [ SecondFactor.view model ]
223− , topButton = Just simpleBackButton
224− }
168+ let
169+ p =
170+ EditCipher.page EditCipherMsg
171+ in
172+ { title = p.title model
173+ , body = p.view model
174+ , topButton = Just (BackButton { action = PopView, icon = Just "close" })
175+ }
225176
226177
227178main : Program () Model Msg
@@ -250,7 +201,7 @@ subscriptions model =
250201 LoadCiphers ciphers
251202
252203 Bridge.LoginSuccessful ->
253− LoginSuccessful
204+ OpenCiphersScreen
254205
255206 Bridge.Reset ->
256207 Reset
@@ -284,17 +235,6 @@ subscriptions model =
284235
285236 Bridge.Totp totp ->
286237 GlobalEvents.DecodedTotp totp |> FireGlobalEvent
287−
288− Bridge.NeedsSecondFactor secondFactors ->
289− case secondFactors of
290− [ provider ] ->
291− SelectSecondFactor
292− { provider = provider
293− , requestFromServer = False
294− }
295−
296− _ ->
297− ShowSecondFactorSelect secondFactors
298238 )
299239 ]
300240 ++ optional (List.isEmpty model.notifications |> not) (Time.every 1000 (\t -> ClearNotification { currentTime = t }))
@@ -303,32 +243,26 @@ subscriptions model =
303243 |> List.map
304244 (\x ->
305245 case x of
306− LoginModel _ ->
307− Sub.none
246+ LoginModel m ->
247+ (Login.page loginCallbacks LoginMsg).subscriptions m
308248
309249 LoadingPage ->
310250 Sub.none
311251
312− CiphersModel _ ->
313− Sub.none
252+ CiphersModel m ->
253+ (Ciphers.page ciphersCallbacks CiphersMsg).subscriptions m
314254
315− EditCipherModel _ ->
316− Sub.none
255+ EditCipherModel m ->
256+ (EditCipher.page EditCipherMsg).subscriptions m
317257
318− MasterPasswordModel _ ->
319− Sub.none
258+ MasterPasswordModel m ->
259+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).subscriptions m
320260
321261 CipherModel m ->
322− Cipher.subscriptions m
323−
324− CaptchaModel _ ->
325− Sub.none
262+ (Cipher.page cipherCallbacks CipherMsg).subscriptions m
326263
327− SecondFactorSelectModel _ ->
328− Sub.none
329−
330− SecondFactorModel _ ->
331− Sub.none
264+ CaptchaModel m ->
265+ (Captcha.page captchaCallbacks CaptchaMsg).subscriptions m
332266 )
333267 )
334268 )
@@ -349,10 +283,7 @@ view : Model -> Html Msg
349283view model =
350284 div []
351285 (maybeList (List.head model.notifications) (Lazy.lazy2 Notification.notification CloseNotification)
352− ++ [ Lazy.lazy2 Navigation.showNavigationView
353− model.pageStack
354− (showPage (Maybe.withDefault "" model.userEmail))
355− ]
286+ ++ [ Lazy.lazy2 Navigation.showNavigationView model.pageStack (showPage (Maybe.withDefault "" model.userEmail)) ]
356287 )
357288
358289
@@ -364,6 +295,12 @@ mapHead f (Nonempty x xx) =
364295doNotStoreInHistory : PageModel -> Bool
365296doNotStoreInHistory page =
366297 case page of
298+ LoginModel _ ->
299+ True
300+
301+ MasterPasswordModel _ ->
302+ True
303+
367304 LoadingPage ->
368305 True
369306
@@ -374,9 +311,19 @@ doNotStoreInHistory page =
374311update : Msg -> Model -> ( Model, Cmd Msg )
375312update msg model =
376313 let
377− updatePageStackHead : PageModel -> Model
378− updatePageStackHead mdl =
379− { model | pageStack = mapHead (always mdl) model.pageStack }
314+ processPage : (pageModel -> PageModel) -> ( Result String pageModel, Cmd Msg ) -> ( Model, Cmd Msg )
315+ processPage liftModel ( resultModel, cmd ) =
316+ case resultModel of
317+ Ok mdl ->
318+ ( { model | pageStack = mapHead (always (liftModel mdl)) model.pageStack }, cmd )
319+
320+ Err err ->
321+ appendNotification
322+ { title = "An error had occured"
323+ , message = err
324+ , severity = Notification.Error
325+ }
326+ model
380327
381328 appendPageStack : PageModel -> Model
382329 appendPageStack mdl =
@@ -385,8 +332,19 @@ update msg model =
385332 keepStackWith : PageModel -> Model
386333 keepStackWith mdl =
387334 { model | pageStack = Nonempty.cons mdl model.pageStack }
335+
336+ currentPage =
337+ model.pageStack |> Nonempty.head
388338 in
389339 case msg of
340+ RecieveMessage x ->
341+ appendNotification
342+ { title = "Got a message!"
343+ , message = x
344+ , severity = Notification.Info
345+ }
346+ model
347+
390348 ShowError title err ->
391349 appendNotification
392350 { title = title
@@ -413,10 +371,25 @@ update msg model =
413371 , FFI.sendBridge (Bridge.Login data)
414372 )
415373
374+ LoginMsg imsg ->
375+ case currentPage of
376+ LoginModel page ->
377+ (Login.page loginCallbacks LoginMsg).update imsg page |> processPage LoginModel
378+
379+ _ ->
380+ ( model, Cmd.none )
381+
382+ CiphersMsg imsg ->
383+ case currentPage of
384+ CiphersModel page ->
385+ (Ciphers.page ciphersCallbacks CiphersMsg).update imsg page |> processPage CiphersModel
386+
387+ _ ->
388+ ( model, Cmd.none )
389+
416390 ShowLoginPage ->
417− ( Login.init loginCallbacks |> LoginModel |> appendPageStack
418− , Cmd.none
419− )
391+ (Login.page loginCallbacks LoginMsg).init ()
392+ |> Tuple.mapFirst (\pageModel -> appendPageStack <| LoginModel pageModel)
420393
421394 LoadCiphers ciphers ->
422395 let
@@ -432,6 +405,9 @@ update msg model =
432405 _ ->
433406 False
434407 )
408+
409+ ( pageModel, cmd ) =
410+ (Ciphers.page ciphersCallbacks CiphersMsg).init ciphers
435411 in
436412 ( if ciphersShown then
437413 { model
@@ -440,8 +416,8 @@ update msg model =
440416 |> Nonempty.map
441417 (\x ->
442418 case x of
443− CiphersModel pageModel ->
444− CiphersModel (Ciphers.updateCiphers ciphers pageModel)
419+ CiphersModel _ ->
420+ CiphersModel pageModel
445421
446422 other ->
447423 other
@@ -449,24 +425,19 @@ update msg model =
449425 }
450426
451427 else
452− appendPageStack <| CiphersModel (Ciphers.init ciphersCallbacks ciphers)
453− , Cmd.none
428+ appendPageStack <| CiphersModel pageModel
429+ , cmd
454430 )
455431
456− LoginSuccessful ->
457− ( { model | pageStack = Nonempty.singleton LoadingPage }
458− , Cmd.batch [ sendBridge Bridge.NeedCiphersList, sendBridge Bridge.NeedEmail ]
459− )
432+ OpenCiphersScreen ->
433+ ( appendPageStack <| LoadingPage, Cmd.batch [ sendBridge Bridge.NeedCiphersList, sendBridge Bridge.NeedEmail ] )
460434
461435 Reset ->
462436 init
463437
464438 ShowMasterPasswordPage { server, login } ->
465− ( MasterPassword.init masterPasswordCallbacks { server = server, login = login }
466− |> MasterPasswordModel
467− |> appendPageStack
468− , Cmd.none
469− )
439+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).init { server = server, login = login }
440+ |> Tuple.mapFirst (\pageModel -> appendPageStack <| MasterPasswordModel pageModel)
470441
471442 NeedsReset ->
472443 ( appendPageStack <| LoadingPage, sendBridge Bridge.NeedsReset )
@@ -474,8 +445,32 @@ update msg model =
474445 SendMasterPassword { password } ->
475446 ( appendPageStack <| LoadingPage, sendBridge (Bridge.SendMasterPassword password) )
476447
448+ MasterPasswordMsg imsg ->
449+ case currentPage of
450+ MasterPasswordModel page ->
451+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).update imsg page |> processPage MasterPasswordModel
452+
453+ _ ->
454+ ( model, Cmd.none )
455+
456+ CipherMsg imsg ->
457+ case currentPage of
458+ CipherModel page ->
459+ (Cipher.page cipherCallbacks CipherMsg).update imsg page |> processPage CipherModel
460+
461+ _ ->
462+ ( model, Cmd.none )
463+
464+ EditCipherMsg imsg ->
465+ case currentPage of
466+ EditCipherModel page ->
467+ (EditCipher.page EditCipherMsg).update imsg page |> processPage EditCipherModel
468+
469+ _ ->
470+ ( model, Cmd.none )
471+
477472 ShowCipherPage cipher ->
478− Cipher.init cipherCallbacks cipher
473+ (Cipher.page cipherCallbacks CipherMsg).init cipher
479474 |> Tuple.mapFirst (\pageModel -> appendPageStack <| CipherModel pageModel)
480475
481476 RequestCipher id ->
@@ -496,7 +491,16 @@ update msg model =
496491 ( model, FFI.sendBridge (Bridge.Open uri) )
497492
498493 ShowCaptcha uri ->
499− ( Captcha.init uri |> CaptchaModel |> appendPageStack, Cmd.none )
494+ (Captcha.page captchaCallbacks CaptchaMsg).init uri
495+ |> Tuple.mapFirst (\pageModel -> keepStackWith <| CaptchaModel pageModel)
496+
497+ CaptchaMsg imsg ->
498+ case currentPage of
499+ CaptchaModel page ->
500+ (Captcha.page captchaCallbacks CaptchaMsg).update imsg page |> processPage CaptchaModel
501+
502+ _ ->
503+ ( model, Cmd.none )
500504
501505 ClearNotification { currentTime } ->
502506 if Time.posixToMillis model.lastNotificationTime + notificationLingerSeconds * 1000 > Time.posixToMillis currentTime then
@@ -510,8 +514,11 @@ update msg model =
510514 UpdateLastNotificatioTime time ->
511515 ( { model | lastNotificationTime = time }, Cmd.none )
512516
517+ Noop ->
518+ ( model, Cmd.none )
519+
513520 EditCipher cipher ->
514− EditCipher.init { fullCipher = cipher, callbacks = editCipherCallbacks }
521+ (EditCipher.page EditCipherMsg).init { fullCipher = cipher, callbacks = editCipherCallbacks }
515522 |> Tuple.mapFirst (\pageModel -> keepStackWith <| EditCipherModel pageModel)
516523
517524 UpdateCipher cipher ->
@@ -542,28 +549,22 @@ update msg model =
542549 LoadingPage
543550
544551 LoginModel m ->
545− LoginModel m
552+ (Login.page loginCallbacks LoginMsg).event m ev |> LoginModel
546553
547554 CiphersModel m ->
548− Ciphers.event m ev |> CiphersModel
555+ (Ciphers.page ciphersCallbacks CiphersMsg).event m ev |> CiphersModel
549556
550557 MasterPasswordModel m ->
551− MasterPasswordModel m
558+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).event m ev |> MasterPasswordModel
552559
553560 CipherModel m ->
554− Cipher.event m ev |> CipherModel
561+ (Cipher.page cipherCallbacks CipherMsg).event m ev |> CipherModel
555562
556563 EditCipherModel m ->
557− EditCipher.event m ev |> EditCipherModel
564+ (EditCipher.page EditCipherMsg).event m ev |> EditCipherModel
558565
559566 CaptchaModel m ->
560− CaptchaModel m
561−
562− SecondFactorSelectModel m ->
563− SecondFactorSelectModel m
564−
565− SecondFactorModel m ->
566− SecondFactorModel m
567+ (Captcha.page captchaCallbacks CaptchaMsg).event m ev |> CaptchaModel
567568 )
568569 }
569570 , Cmd.none
@@ -573,14 +574,13 @@ update msg model =
573574 ( model, FFI.sendBridge (Bridge.GeneratePassword cfg) )
574575
575576 OpenNewCipherEditPage t ->
576− EditCipher.init
577+ (EditCipher.page EditCipherMsg).init
577578 { callbacks = createCipherCallbacks
578579 , fullCipher =
579580 { reprompt = 0
580581 , favorite = False
581582 , id = ""
582583 , name = ""
583− , collectionIds = []
584584 , cipher =
585585 case t of
586586 Bridge.LoginType ->
@@ -649,96 +649,10 @@ update msg model =
649649 RequestTotp totp ->
650650 ( model, FFI.sendBridge (Bridge.RequestTotp totp) )
651651
652− ShowSecondFactorSelect secondFactors ->
653− ( SecondFactorSelect.init secondFactorSelectCallbacks secondFactors
654− |> SecondFactorSelectModel
655− |> appendPageStack
656− , Cmd.none
657− )
658−
659− SelectSecondFactor { provider, requestFromServer } ->
660− case Nonempty.toList model.pageStack |> findLoginDetails of
661− Just { email, password, server } ->
662− ( SecondFactor.init
663− { provider = provider
664− , callbacks =
665− { submit =
666− \{ token, remember } ->
667− SubmitLogin
668− { email = email
669− , password = password
670− , server = server
671− , secondFactor =
672− Just
673− { provider = provider
674− , token = token
675− , remember = remember
676− }
677− }
678− }
679− }
680− |> SecondFactorModel
681− |> appendPageStack
682− , Bridge.ChooseSecondFactor
683− { factor = provider
684− , email = email
685− , password = password
686− , server = server
687− , requestFromServer = requestFromServer
688− }
689− |> FFI.sendBridge
690− )
691−
692− Nothing ->
693− ( model, Cmd.none )
694−
695− PageMsg pageMsg ->
696− case ( pageMsg, model.pageStack |> Nonempty.head ) of
697− ( SecondFactorMsg imsg, SecondFactorModel m ) ->
698− SecondFactor.update m imsg
699− |> Tuple.mapBoth
700− (SecondFactorModel >> updatePageStackHead)
701− (Maybe.map pureCmd >> Maybe.withDefault Cmd.none)
702−
703− ( LoginMsg imsg, LoginModel page ) ->
704− Login.update imsg page
705− |> Tuple.mapFirst (LoginModel >> updatePageStackHead)
706−
707− ( CiphersMsg imsg, CiphersModel page ) ->
708− Ciphers.update imsg page
709− |> Tuple.mapFirst (CiphersModel >> updatePageStackHead)
710−
711− ( MasterPasswordMsg imsg, MasterPasswordModel page ) ->
712− MasterPassword.update imsg page
713− |> Tuple.mapFirst (MasterPasswordModel >> updatePageStackHead)
714−
715− ( CipherMsg imsg, CipherModel page ) ->
716− Cipher.update imsg page
717− |> Tuple.mapFirst (CipherModel >> updatePageStackHead)
718−
719− ( EditCipherMsg imsg, EditCipherModel page ) ->
720− EditCipher.update imsg page
721− |> Tuple.mapFirst (EditCipherModel >> updatePageStackHead)
722−
723− ( SecondFactorSelectMsg imsg, SecondFactorSelectModel m ) ->
724− SecondFactorSelect.update imsg m
725− |> Tuple.mapFirst (SecondFactorSelectModel >> updatePageStackHead)
726−
727− _ ->
728− ( model, Cmd.none )
729−
730652
731653loginCallbacks : Login.Callbacks Msg
732654loginCallbacks =
733− { submit =
734− \{ email, server, password } ->
735− SubmitLogin
736− { email = email
737− , password = password
738− , server = server
739− , secondFactor = Nothing
740− }
741− }
655+ { submit = SubmitLogin }
742656
743657
744658ciphersCallbacks : Ciphers.Callbacks Msg
@@ -746,7 +660,6 @@ ciphersCallbacks =
746660 { selected = RequestCipher
747661 , logOut = NeedsReset
748662 , createNewCipher = OpenNewCipherEditPage
749− , lift = CiphersMsg >> PageMsg
750663 }
751664
752665
@@ -765,16 +678,13 @@ createCipherCallbacks =
765678 { save = CreateCipher, generatePassword = GeneratePassword, delete = Nothing }
766679
767680
681+captchaCallbacks : Captcha.Callbacks
682+captchaCallbacks =
683+ {}
684+
685+
768686masterPasswordCallbacks : MasterPassword.Callbacks Msg
769687masterPasswordCallbacks =
770688 { submit = SendMasterPassword
771689 , reset = NeedsReset
772690 }
773−
774−
775−secondFactorSelectCallbacks : SecondFactorSelect.Callbacks Msg
776−secondFactorSelectCallbacks =
777− { selectFactor =
778− \provider ->
779− SelectSecondFactor { provider = provider, requestFromServer = True }
780− }
frontend/Page.elm+15−23
@@ -1,32 +1,24 @@
11module Page exposing (..)
22
3−import Browser
3+import GlobalEvents exposing (Event)
44import Html exposing (..)
55import Html.Events exposing (..)
66
77
8−page :
9− { init : model
10− , view : model -> List (Html msg)
11− , update : msg -> model -> ( model, Cmd (Maybe msg) )
12− }
13− -> Program () model (Maybe msg)
14−page { init, view, update } =
15− Browser.element
16− { subscriptions = always Sub.none
17− , init = \_ -> ( init, Cmd.none )
18− , view = view >> Html.div [] >> Html.map Just
19− , update =
20− \mmsg model ->
21− case mmsg of
22− Nothing ->
23− ( model, Cmd.none )
8+type alias Env =
9+ {}
10+
2411
25− Just msg ->
26− update msg model
27− }
12+type alias Page init model imsg emsg =
13+ (imsg -> emsg)
14+ -> InitializedPage init model imsg emsg
2815
2916
30−noUpdate : a -> model -> ( model, Cmd msg )
31−noUpdate _ model =
32− ( model, Cmd.none )
17+type alias InitializedPage init model imsg emsg =
18+ { init : init -> ( model, Cmd emsg )
19+ , view : model -> List (Html emsg)
20+ , update : imsg -> model -> ( Result String model, Cmd emsg )
21+ , subscriptions : model -> Sub emsg
22+ , title : model -> List (Html emsg)
23+ , event : model -> Event -> model
24+ }
frontend/Pages/Captcha.elm+35−22
@@ -1,4 +1,4 @@
1−module Pages.Captcha exposing (HCaptchSiteKey, Model, init, main, title, view)
1+module Pages.Captcha exposing (Callbacks, HCaptchSiteKey, Model, Msg, page)
22
33import Html exposing (..)
44import Html.Attributes as Attr
@@ -6,15 +6,6 @@ import Page exposing (..)
66import Utils exposing (..)
77
88
9−main : Program () Model (Maybe (Maybe ()))
10−main =
11− page
12− { init = init "10000000-ffff-ffff-ffff-000000000001"
13− , view = view
14− , update = noUpdate
15− }
16−
17−
189type alias HCaptchSiteKey =
1910 String
2011
@@ -24,22 +15,44 @@ type alias Model =
2415 }
2516
2617
27−title : String
28−title =
29− "Captcha"
18+type Msg
19+ = Noop
20+
3021
22+type alias Callbacks =
23+ {}
24+
25+
26+page : Callbacks -> Page HCaptchSiteKey Model Msg emsg
27+page callbacks liftMsg =
28+ { init = \siteKey -> Tuple.mapSecond (Cmd.map liftMsg) (init siteKey)
29+ , view = \model -> view model |> List.map (Html.map liftMsg)
30+ , update = \msg model -> update callbacks liftMsg msg model
31+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
32+ , title = always [ text "Captcha" ]
33+ , event = \model _ -> model
34+ }
3135
32−init : HCaptchSiteKey -> Model
36+
37+init : HCaptchSiteKey -> ( Model, Cmd Msg )
3338init siteKey =
34− { siteKey = siteKey }
39+ ( { siteKey = siteKey }, Cmd.none )
40+
41+
42+update : Callbacks -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
43+update _ _ msg model =
44+ case msg of
45+ Noop ->
46+ ( Ok model, Cmd.none )
47+
48+
49+subscriptions : Model -> Sub Msg
50+subscriptions model =
51+ Sub.none
3552
3653
37−view : Model -> List (Html msg)
54+view : Model -> List (Html Msg)
3855view { siteKey } =
39− [ iframe
40− [ Attr.src ("https://iko.soy/warden/hcaptcha.html?" ++ siteKey)
41− , Attr.style "width" "100%"
42− , Attr.style "height" "80vh"
43− ]
44− []
56+ [ div [ Attr.class "u-embedded-media" ]
57+ [ iframe [ Attr.src ("https://iko.soy/warden/hcaptcha.html?" ++ siteKey) ] [] ]
4558 ]
frontend/Pages/Cipher.elm+51−62
@@ -1,14 +1,4 @@
1−module Pages.Cipher exposing
2− ( Callbacks
3− , Model
4− , Msg
5− , event
6− , init
7− , subscriptions
8− , title
9− , update
10− , view
11− )
1+module Pages.Cipher exposing (..)
122
133import Bridge
144import GlobalEvents
@@ -20,12 +10,11 @@ import Time
2010import Utils exposing (..)
2111
2212
23−type alias Model emsg =
13+type alias Model =
2414 { cipher : Bridge.FullCipher
2515 , passwordHidden : Bool
2616 , cvvHidden : Bool
2717 , decodedTotp : Maybe { code : String, interval : Int }
28− , callbacks : Callbacks emsg
2918 }
3019
3120
@@ -34,7 +23,6 @@ type Msg
3423 | ToggleCVVVisiblity
3524 | Copy String
3625 | Open String
37− | Edit Bridge.FullCipher
3826
3927
4028type alias Callbacks msg =
@@ -45,55 +33,59 @@ type alias Callbacks msg =
4533 }
4634
4735
48−event : Model emsg -> GlobalEvents.Event -> Model emsg
49−event model ev =
50− case ev of
51− GlobalEvents.UpdateCipher c ->
52− { model
53− | cipher =
54− if c.id == model.cipher.id then
55− c
56−
57− else
58− model.cipher
59− }
60−
61− GlobalEvents.DecodedTotp { source, code, interval } ->
62− case model.cipher.cipher of
63− Bridge.LoginCipher { totp } ->
64− if totp == Just source then
65− { model | decodedTotp = Just { code = code, interval = interval } }
66−
67− else
68− model
36+page : Callbacks emsg -> Page Bridge.FullCipher Model Msg emsg
37+page callbacks liftMsg =
38+ { init = \data -> init callbacks data
39+ , view = \model -> view model |> List.map (Html.map liftMsg)
40+ , update = \msg model -> update callbacks liftMsg msg model
41+ , subscriptions = \model -> subscriptions model callbacks
42+ , title =
43+ \{ cipher } ->
44+ [ text cipher.name
45+ , span [ Attr.class "u-float-right" ] [ iconButton "edit" (callbacks.edit cipher) ]
46+ ]
47+ , event =
48+ \model ev ->
49+ case ev of
50+ GlobalEvents.UpdateCipher c ->
51+ { model
52+ | cipher =
53+ if c.id == model.cipher.id then
54+ c
55+
56+ else
57+ model.cipher
58+ }
59+
60+ GlobalEvents.DecodedTotp { source, code, interval } ->
61+ case model.cipher.cipher of
62+ Bridge.LoginCipher { totp } ->
63+ if totp == Just source then
64+ { model | decodedTotp = Just { code = code, interval = interval } }
65+
66+ else
67+ model
68+
69+ _ ->
70+ model
6971
7072 _ ->
7173 model
72−
73− _ ->
74− model
75−
76−
77−title : Model emsg -> List (Html Msg)
78−title { cipher } =
79− [ text cipher.name
80− , span [ Attr.class "u-float-right" ] [ iconButton "edit" (Edit cipher) ]
81− ]
74+ }
8275
8376
84−init : Callbacks emsg -> Bridge.FullCipher -> ( Model emsg, Cmd emsg )
85−init callbacks cipher =
77+init : Callbacks emsg -> Bridge.FullCipher -> ( Model, Cmd emsg )
78+init { needTotp } cipher =
8679 ( { cipher = cipher
8780 , passwordHidden = True
8881 , cvvHidden = True
8982 , decodedTotp = Nothing
90− , callbacks = callbacks
9183 }
9284 , case cipher.cipher of
9385 Bridge.LoginCipher { totp } ->
9486 case totp of
9587 Just x ->
96− callbacks.needTotp x |> pureCmd
88+ needTotp x |> pureCmd
9789
9890 Nothing ->
9991 Cmd.none
@@ -103,32 +95,29 @@ init callbacks cipher =
10395 )
10496
10597
106−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
107−update msg model =
98+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
99+update { copy, open } _ msg model =
108100 case msg of
109101 TogglePasswordVisiblity ->
110− ( { model | passwordHidden = not model.passwordHidden }, Cmd.none )
102+ ( Ok { model | passwordHidden = not model.passwordHidden }, Cmd.none )
111103
112104 ToggleCVVVisiblity ->
113− ( { model | cvvHidden = not model.cvvHidden }, Cmd.none )
105+ ( Ok { model | cvvHidden = not model.cvvHidden }, Cmd.none )
114106
115107 Copy text ->
116− ( model, model.callbacks.copy text |> pureCmd )
108+ ( Ok model, copy text |> pureCmd )
117109
118110 Open uri ->
119− ( model, model.callbacks.open uri |> pureCmd )
120−
121− Edit cipher ->
122− ( model, model.callbacks.edit cipher |> pureCmd )
111+ ( Ok model, open uri |> pureCmd )
123112
124113
125−subscriptions : Model msg -> Sub msg
126−subscriptions { cipher, callbacks } =
114+subscriptions : Model -> Callbacks msg -> Sub msg
115+subscriptions { cipher } { needTotp } =
127116 case cipher.cipher of
128117 Bridge.LoginCipher { totp } ->
129118 case totp of
130119 Just x ->
131− Time.every 1000 (always (callbacks.needTotp x))
120+ Time.every 1000 (always (needTotp x))
132121
133122 Nothing ->
134123 Sub.none
@@ -147,7 +136,7 @@ row { name, value, nameIcon, icons } =
147136 ]
148137
149138
150−view : Model emsg -> List (Html Msg)
139+view : Model -> List (Html Msg)
151140view { passwordHidden, cipher, cvvHidden, decodedTotp } =
152141 (case cipher.cipher of
153142 Bridge.LoginCipher { username, password, uris, totp } ->
frontend/Pages/Ciphers.elm+129−140
@@ -1,15 +1,4 @@
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− )
1+module Pages.Ciphers exposing (Callbacks, Model, Msg, menuConfig, page)
132
143import Bridge
154import GlobalEvents
@@ -25,7 +14,7 @@ import Search
2514import Utils exposing (..)
2615
2716
28−type alias Model emsg =
17+type alias Model =
2918 { ciphers : Bridge.Sub_LoadCiphers_List
3019 , search : String
3120 , menuVisible : Bool
@@ -33,15 +22,9 @@ type alias Model emsg =
3322 , scroll : InfiniteScroll.Model Msg
3423 , sublist : { end : Int }
3524 , cipherSelectDropdownVisivble : Bool
36− , callbacks : Callbacks emsg
3725 }
3826
3927
40−updateCiphers : Bridge.Sub_LoadCiphers_List -> Model emsg -> Model emsg
41−updateCiphers ciphers model =
42− { model | ciphers = ciphers }
43−
44−
4528type CiphersFilter
4629 = AllCiphers
4730 | SpecificCiphers Bridge.CipherType
@@ -57,7 +40,7 @@ applyCipherFilter filter list =
5740 list |> List.filter (.cipherType >> (==) t)
5841
5942
60−menuConfig : String -> Model emsg -> Navigation.MenuConfig Msg
43+menuConfig : String -> Model -> Navigation.MenuConfig Msg
6144menuConfig email { ciphersListFilter, menuVisible } =
6245 { title = email
6346 , items =
@@ -103,135 +86,139 @@ type Msg
10386 | InfiniteScrollMsg InfiniteScroll.Msg
10487 | CreateNewCipher Bridge.CipherType
10588 | ToggleSelectCipherTypeVisible
106− | SelectCipher String
10789
10890
10991type alias Callbacks msg =
11092 { selected : String -> msg
11193 , logOut : msg
11294 , createNewCipher : Bridge.CipherType -> msg
113− , lift : Msg -> msg
11495 }
11596
11697
117−event : Model emsg -> GlobalEvents.Event -> Model emsg
118−event model ev =
119− case ev of
120− GlobalEvents.UpdateCipher c ->
121− { model
122− | ciphers =
123− model.ciphers
124− |> List.map
125− (\cipher ->
126− if cipher.id == c.id then
127− { cipher | name = c.name }
128−
129− else
130− cipher
131− )
132− }
133−
134− _ ->
135− model
136−
137−
138−title : Model emsg -> List (Html Msg)
139−title { ciphersListFilter, cipherSelectDropdownVisivble } =
140− [ text
141− (case ciphersListFilter of
142− AllCiphers ->
143− "All items"
144−
145− SpecificCiphers cipherType ->
146− case cipherType of
147− Bridge.CardType ->
148− "Cards"
149−
150− Bridge.IdentityType ->
151− "Contacts"
152−
153− Bridge.LoginType ->
154− "Passwords"
155−
156− Bridge.NoteType ->
157− "Notes"
158− )
159− , span [ Attr.class "u-float-right" ]
160− [ span [ Attr.class "p-contextual-menu" ]
161− ([ span [] [ iconButton "plus" ToggleSelectCipherTypeVisible ]
162− ]
163− ++ optionals cipherSelectDropdownVisivble
164− [ div
165− [ Attr.class "overlay"
166− , Ev.onClick ToggleSelectCipherTypeVisible
167− ]
168− []
169− , span
170− [ Attr.class "p-contextual-menu__dropdown"
171− , Attr.attribute "aria-hidden" "false"
172− ]
173− (let
174− create t =
175− CreateNewCipher t
176− in
177− [ button
178− [ Attr.class "p-contextual-menu__link"
179− , Ev.onClick (create Bridge.LoginType)
180− ]
181− [ text "Login" ]
182− , button
183− [ Attr.class "p-contextual-menu__link"
184− , Ev.onClick (create Bridge.CardType)
185− ]
186− [ text "Card" ]
187− , button
188− [ Attr.class "p-contextual-menu__link"
189− , Ev.onClick (create Bridge.NoteType)
190− ]
191− [ text "Note" ]
192− , button
193− [ Attr.class "p-contextual-menu__link"
194− , Ev.onClick (create Bridge.IdentityType)
98+page : Callbacks emsg -> Page Bridge.Sub_LoadCiphers_List Model Msg emsg
99+page callbacks liftMsg =
100+ { init = \ciphers -> Tuple.mapSecond (Cmd.map liftMsg) (init ciphers)
101+ , view = view callbacks liftMsg
102+ , update = \msg model -> update callbacks liftMsg msg model
103+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
104+ , title =
105+ \{ ciphersListFilter, cipherSelectDropdownVisivble } ->
106+ [ text
107+ (case ciphersListFilter of
108+ AllCiphers ->
109+ "All items"
110+
111+ SpecificCiphers cipherType ->
112+ case cipherType of
113+ Bridge.CardType ->
114+ "Cards"
115+
116+ Bridge.IdentityType ->
117+ "Identities"
118+
119+ Bridge.LoginType ->
120+ "Passwords"
121+
122+ Bridge.NoteType ->
123+ "Notes"
124+ )
125+ , span [ Attr.class "u-float-right" ]
126+ [ span [ Attr.class "p-contextual-menu" ]
127+ ([ span [] [ iconButton "plus" (liftMsg ToggleSelectCipherTypeVisible) ]
128+ ]
129+ ++ optionals cipherSelectDropdownVisivble
130+ [ div
131+ [ Attr.class "overlay"
132+ , Ev.onClick (liftMsg ToggleSelectCipherTypeVisible)
133+ ]
134+ []
135+ , span
136+ [ Attr.class "p-contextual-menu__dropdown"
137+ , Attr.attribute "aria-hidden" "false"
138+ ]
139+ (let
140+ create t =
141+ liftMsg (CreateNewCipher t)
142+ in
143+ [ button
144+ [ Attr.class "p-contextual-menu__link"
145+ , Ev.onClick (create Bridge.LoginType)
146+ ]
147+ [ text "Login" ]
148+ , button
149+ [ Attr.class "p-contextual-menu__link"
150+ , Ev.onClick (create Bridge.CardType)
151+ ]
152+ [ text "Card" ]
153+ , button
154+ [ Attr.class "p-contextual-menu__link"
155+ , Ev.onClick (create Bridge.NoteType)
156+ ]
157+ [ text "Note" ]
158+ , button
159+ [ Attr.class "p-contextual-menu__link"
160+ , Ev.onClick (create Bridge.IdentityType)
161+ ]
162+ [ text "Contact" ]
163+ ]
164+ )
195165 ]
196− [ text "Contact" ]
197− ]
198− )
199− ]
200− )
201− ]
202− ]
166+ )
167+ ]
168+ ]
169+ , event =
170+ \model ev ->
171+ case ev of
172+ GlobalEvents.UpdateCipher c ->
173+ { model
174+ | ciphers =
175+ model.ciphers
176+ |> List.map
177+ (\cipher ->
178+ if cipher.id == c.id then
179+ { cipher | name = c.name }
180+
181+ else
182+ cipher
183+ )
184+ }
185+
186+ _ ->
187+ model
188+ }
203189
204190
205−init : Callbacks emsg -> Bridge.Sub_LoadCiphers_List -> Model emsg
206−init callbacks ciphers =
207− { ciphers = ciphers
208− , search = ""
209− , menuVisible = False
210− , ciphersListFilter = AllCiphers
211− , scroll = InfiniteScroll.init (LoadMore >> pureCmd)
212− , sublist = { end = 20 }
213− , cipherSelectDropdownVisivble = False
214− , callbacks = callbacks
215− }
191+init : Bridge.Sub_LoadCiphers_List -> ( Model, Cmd Msg )
192+init ciphers =
193+ ( { ciphers = ciphers
194+ , search = ""
195+ , menuVisible = False
196+ , ciphersListFilter = AllCiphers
197+ , scroll = InfiniteScroll.init (LoadMore >> pureCmd)
198+ , sublist = { end = 20 }
199+ , cipherSelectDropdownVisivble = False
200+ }
201+ , Cmd.none
202+ )
216203
217204
218−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
219−update msg model =
205+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
206+update { logOut, createNewCipher } liftMsg msg model =
220207 case msg of
221208 Noop ->
222− ( model, Cmd.none )
209+ ( Ok model, Cmd.none )
223210
224211 UpdateSearch s ->
225− ( { model | search = s }, Cmd.none )
212+ ( Ok { model | search = s }, Cmd.none )
226213
227214 ToggleMenu ->
228− ( { model | menuVisible = not model.menuVisible }, Cmd.none )
215+ ( Ok { model | menuVisible = not model.menuVisible }, Cmd.none )
229216
230217 UpdateFilter filter ->
231− ( { model | ciphersListFilter = filter, menuVisible = False }, Cmd.none )
218+ ( Ok { model | ciphersListFilter = filter, menuVisible = False }, Cmd.none )
232219
233220 LogOut ->
234− ( model, pureCmd model.callbacks.logOut )
221+ ( Ok model, pureCmd logOut )
235222
236223 LoadMore direction ->
237224 let
@@ -243,54 +230,56 @@ update msg model =
243230 InfiniteScroll.Bottom ->
244231 { end = min (model.sublist.end + 10) (List.length model.ciphers) }
245232 in
246− ( { model | sublist = newSublist, scroll = InfiniteScroll.stopLoading model.scroll }, Cmd.none )
233+ ( Ok { model | sublist = newSublist, scroll = InfiniteScroll.stopLoading model.scroll }, Cmd.none )
247234
248235 InfiniteScrollMsg msg_ ->
249236 let
250237 ( scroll, cmd ) =
251238 InfiniteScroll.update InfiniteScrollMsg msg_ model.scroll
252239 in
253− ( { model | scroll = scroll }, Cmd.map model.callbacks.lift cmd )
240+ ( Ok { model | scroll = scroll }, Cmd.map liftMsg cmd )
254241
255242 CreateNewCipher t ->
256− ( { model | cipherSelectDropdownVisivble = False }, pureCmd (model.callbacks.createNewCipher t) )
243+ ( Ok { model | cipherSelectDropdownVisivble = False }, pureCmd (createNewCipher t) )
257244
258245 ToggleSelectCipherTypeVisible ->
259− ( { model | cipherSelectDropdownVisivble = not model.cipherSelectDropdownVisivble }
246+ ( Ok { model | cipherSelectDropdownVisivble = not model.cipherSelectDropdownVisivble }
260247 , Cmd.none
261248 )
262249
263− SelectCipher id ->
264− ( model, model.callbacks.selected id |> pureCmd )
250+
251+subscriptions : Model -> Sub Msg
252+subscriptions _ =
253+ Sub.none
265254
266255
267−view : Model emsg -> List (Html Msg)
268−view { ciphers, search, ciphersListFilter, sublist } =
256+view : Callbacks emsg -> (Msg -> emsg) -> Model -> List (Html emsg)
257+view { selected } liftMsg { ciphers, search, ciphersListFilter, sublist } =
269258 [ input
270259 [ Attr.type_ "search"
271260 , Attr.value search
272− , Ev.onInput UpdateSearch
261+ , Ev.onInput (liftMsg << UpdateSearch)
273262 , Attr.placeholder "Search"
274263 , Attr.attribute "autocomplete" "off"
275264 ]
276265 []
277266 , ciphers
278267 |> applyCipherFilter ciphersListFilter
279− |> Search.searchList search .name (List.take sublist.end >> Lazy.lazy showCiphers)
268+ |> Search.searchList search .name (List.take sublist.end >> Lazy.lazy3 showCiphers liftMsg selected)
280269 ]
281270
282271
283−showCiphers : Bridge.Sub_LoadCiphers_List -> Html Msg
284−showCiphers ciphers =
285− Keyed.ul [ Attr.class "p-list--divided", InfiniteScroll.infiniteScroll InfiniteScrollMsg, Attr.class "ciphers-list no-scroll-bar" ]
272+showCiphers : (Msg -> msg) -> (String -> msg) -> Bridge.Sub_LoadCiphers_List -> Html msg
273+showCiphers liftMsg selected ciphers =
274+ Keyed.ul [ Attr.class "p-list--divided", InfiniteScroll.infiniteScroll (InfiniteScrollMsg >> liftMsg), Attr.class "ciphers-list no-scroll-bar" ]
286275 (ciphers
287276 |> List.map
288277 (\{ name, date, id } ->
289278 ( id
290− , li [ Attr.class "p-list__item cipher-row", Ev.onClick (SelectCipher id) ]
279+ , li [ Attr.class "p-list__item cipher-row", Ev.onClick (selected id) ]
291280 [ div [ Attr.class "cipher-row-container" ]
292281 [ p [] [ text name ]
293− , p [ Attr.class "p-text--small u-align-text--right u-text--muted hide-in-test" ] [ text date ]
282+ , p [ Attr.class "p-text--small u-align-text--right u-text--muted" ] [ text date ]
294283 ]
295284 ]
296285 )
frontend/Pages/EditCipher.elm+61−61
@@ -1,4 +1,4 @@
1−module Pages.EditCipher exposing (Callbacks, Model, Msg, event, init, title, update, view)
1+module Pages.EditCipher exposing (..)
22
33import Bridge
44import GlobalEvents
@@ -8,7 +8,6 @@ import Html.Events as Ev
88import Logic.Cipher exposing (normalizeCipher)
99import Modal exposing (modal)
1010import Page exposing (..)
11−import Pages.MasterPassword exposing (Msg)
1211import Pages.PasswordGenerator as PasswordGenerator
1312import Utils exposing (..)
1413
@@ -27,7 +26,6 @@ type Msg
2726 | CloseGeneratePassword
2827 | GeneratePassword Bridge.PasswordGeneratorConfig
2928 | Delete
30− | Save
3129
3230
3331type alias Callbacks msg =
@@ -37,50 +35,56 @@ type alias Callbacks msg =
3735 }
3836
3937
40−title : Model msg -> List (Html Msg)
41−title { fullCipher } =
42− [ text fullCipher.name
43− , span [ Attr.class "u-float-right" ]
44− [ iconButton "task-outstanding" Save
45− ]
46− ]
47−
48−
49−event : Model msg -> GlobalEvents.Event -> Model msg
50−event model ev =
51− case ev of
52− GlobalEvents.UpdateCipher c ->
53− { model
54− | fullCipher =
55− if c.id == model.fullCipher.id then
56− c
57−
58− else
59− model.fullCipher
60− }
61−
62− GlobalEvents.GeneratedPassword password ->
63− let
64− fullCipher =
65− model.fullCipher
66− in
67− { model
68− | passwordGenerator = Nothing
69− , fullCipher =
70− { fullCipher
71− | cipher =
72− case fullCipher.cipher of
73− Bridge.LoginCipher c ->
74− Bridge.LoginCipher
75− { c | password = Just password }
76−
77− c ->
78− c
38+page : Page { fullCipher : Bridge.FullCipher, callbacks : Callbacks emsg } (Model emsg) Msg emsg
39+page liftMsg =
40+ { init = init
41+ , view = \model -> view model |> List.map (Html.map liftMsg)
42+ , update = \msg model -> update liftMsg msg model
43+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
44+ , title =
45+ \{ fullCipher, callbacks } ->
46+ [ text fullCipher.name
47+ , span [ Attr.class "u-float-right" ]
48+ [ iconButton "task-outstanding"
49+ (callbacks.save { fullCipher | cipher = normalizeCipher fullCipher.cipher })
50+ ]
51+ ]
52+ , event =
53+ \model ev ->
54+ case ev of
55+ GlobalEvents.UpdateCipher c ->
56+ { model
57+ | fullCipher =
58+ if c.id == model.fullCipher.id then
59+ c
60+
61+ else
62+ model.fullCipher
7963 }
80− }
8164
82− _ ->
83− model
65+ GlobalEvents.GeneratedPassword password ->
66+ let
67+ fullCipher =
68+ model.fullCipher
69+ in
70+ { model
71+ | passwordGenerator = Nothing
72+ , fullCipher =
73+ { fullCipher
74+ | cipher =
75+ case fullCipher.cipher of
76+ Bridge.LoginCipher c ->
77+ Bridge.LoginCipher
78+ { c | password = Just password }
79+
80+ c ->
81+ c
82+ }
83+ }
84+
85+ _ ->
86+ model
87+ }
8488
8589
8690init : { fullCipher : Bridge.FullCipher, callbacks : Callbacks emsg } -> ( Model emsg, Cmd emsg )
@@ -93,42 +97,38 @@ init { fullCipher, callbacks } =
9397 )
9498
9599
96−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
97−update msg model =
100+update : (Msg -> emsg) -> Msg -> Model emsg -> ( Result String (Model emsg), Cmd emsg )
101+update _ msg model =
98102 case msg of
99103 EditCipher fullCipher ->
100− ( { model | fullCipher = fullCipher }, Cmd.none )
104+ ( Ok { model | fullCipher = fullCipher }, Cmd.none )
101105
102106 PasswordGeneratorMsg m ->
103− ( { model | passwordGenerator = Maybe.map (PasswordGenerator.update m) model.passwordGenerator }
107+ ( Ok { model | passwordGenerator = Maybe.map (PasswordGenerator.update m) model.passwordGenerator }
104108 , Cmd.none
105109 )
106110
107111 OpenGeneratePasword ->
108− ( { model | passwordGenerator = Just PasswordGenerator.init }
112+ ( Ok { model | passwordGenerator = Just PasswordGenerator.init }
109113 , Cmd.none
110114 )
111115
112116 CloseGeneratePassword ->
113− ( { model | passwordGenerator = Nothing }, Cmd.none )
117+ ( Ok { model | passwordGenerator = Nothing }, Cmd.none )
114118
115119 GeneratePassword cfg ->
116− ( model, pureCmd (model.callbacks.generatePassword cfg) )
120+ ( Ok model, pureCmd (model.callbacks.generatePassword cfg) )
117121
118122 Delete ->
119− ( model
123+ ( Ok model
120124 , Maybe.withDefault Cmd.none
121125 (Maybe.map (\f -> f model.fullCipher |> pureCmd) model.callbacks.delete)
122126 )
123127
124− Save ->
125− let
126− { fullCipher } =
127− model
128− in
129− ( model
130− , model.callbacks.save { fullCipher | cipher = normalizeCipher fullCipher.cipher } |> pureCmd
131− )
128+
129+subscriptions : Model emsg -> Sub Msg
130+subscriptions _ =
131+ Sub.none
132132
133133
134134heading : String -> String -> Html msg
frontend/Pages/Login.elm+42−38
@@ -1,4 +1,4 @@
1−module Pages.Login exposing (Callbacks, Model, Msg, init, title, update, view)
1+module Pages.Login exposing (Callbacks, Model, Msg, page)
22
33import Html exposing (..)
44import Html.Attributes as Attr
@@ -7,11 +7,10 @@ import Page exposing (..)
77import Utils exposing (..)
88
99
10−type alias Model emsg =
10+type alias Model =
1111 { server : String
1212 , email : String
1313 , password : String
14− , callbacks : Callbacks emsg
1514 }
1615
1716
@@ -27,49 +26,53 @@ type alias Callbacks emsg =
2726 }
2827
2928
30−title : String
31−title =
32− "Log into bitwarden"
29+page : Callbacks emsg -> Page () Model Msg emsg
30+page callbacks liftMsg =
31+ { init = \() -> Tuple.mapSecond (Cmd.map liftMsg) init
32+ , view = \model -> view model |> List.map (Html.map liftMsg)
33+ , update = \msg model -> update callbacks liftMsg msg model
34+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
35+ , title = always [ text "Log into bitwarden" ]
36+ , event = \model _ -> model
37+ }
3338
3439
35−init : Callbacks emsg -> Model emsg
36−init callbacks =
37− { server = "https://vault.bitwarden.com"
38− , email = ""
39− , password = ""
40− , callbacks = callbacks
41− }
40+init : ( Model, Cmd Msg )
41+init =
42+ ( { server = "https://vault.bitwarden.com"
43+ , email = ""
44+ , password = ""
45+ }
46+ , Cmd.none
47+ )
4248
4349
44−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
45−update msg model =
50+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
51+update { submit } liftMsg msg model =
4652 case msg of
4753 UpdateEmail email ->
48− ( { model | email = email }, Cmd.none )
54+ ( Ok { model | email = email }, Cmd.none )
4955
5056 UpdateServer server ->
51− ( { model | server = server }, Cmd.none )
57+ ( Ok { model | server = server }, Cmd.none )
5258
5359 UpdatePassword password ->
54− ( { model | password = password }, Cmd.none )
60+ ( Ok { model | password = password }, Cmd.none )
5561
5662 Submit ->
57− ( model
58− , pureCmd
59− (model.callbacks.submit
60− { email = model.email
61− , password = model.password
62− , server = model.server
63− }
64− )
65− )
66−
67−
68−view : Model emsg -> List (Html Msg)
63+ ( Ok model, pureCmd (submit model) )
64+
65+
66+subscriptions : Model -> Sub Msg
67+subscriptions model =
68+ Sub.none
69+
70+
71+view : Model -> List (Html Msg)
6972view model =
7073 [ form [ Ev.onSubmit Submit ]
71− [ label [] [ text "Server" ]
72− , input
74+ ([ label [] [ text "Server" ]
75+ , input
7376 [ Attr.type_ "url"
7477 , Attr.value model.server
7578 , Ev.onInput UpdateServer
@@ -77,8 +80,8 @@ view model =
7780 , Attr.required True
7881 ]
7982 []
80− , label [] [ text "Email" ]
81− , input
83+ , label [] [ text "Email" ]
84+ , input
8285 [ Attr.type_ "email"
8386 , Attr.value model.email
8487 , Ev.onInput UpdateEmail
@@ -86,8 +89,8 @@ view model =
8689 , Attr.required True
8790 ]
8891 []
89− , label [] [ text "Password" ]
90− , input
92+ , label [] [ text "Password" ]
93+ , input
9194 [ Attr.type_ "password"
9295 , Attr.value model.password
9396 , Ev.onInput UpdatePassword
@@ -95,6 +98,7 @@ view model =
9598 , Attr.required True
9699 ]
97100 []
98− , alignRight [ button [ Attr.type_ "submit" ] [ text "Log in" ] ]
99− ]
101+ ]
102+ ++ [ alignRight [ button [ Attr.type_ "submit" ] [ text "Log in" ] ] ]
103+ )
100104 ]
frontend/Pages/MasterPassword.elm+30−19
@@ -1,4 +1,4 @@
1−module Pages.MasterPassword exposing (Callbacks, Model, Msg, init, title, update, view)
1+module Pages.MasterPassword exposing (..)
22
33import Html exposing (..)
44import Html.Attributes as Attr
@@ -7,11 +7,10 @@ import Page exposing (..)
77import Utils exposing (..)
88
99
10−type alias Model emsg =
10+type alias Model =
1111 { server : String
1212 , email : String
1313 , password : String
14− , callbacks : Callbacks emsg
1514 }
1615
1716
@@ -27,34 +26,46 @@ type alias Callbacks emsg =
2726 }
2827
2928
30−title : String
31−title =
32− "Enter master password"
29+page : Callbacks emsg -> Page { server : String, login : String } Model Msg emsg
30+page callbacks liftMsg =
31+ { init = \cfg -> Tuple.mapSecond (Cmd.map liftMsg) (init cfg)
32+ , view = \model -> view model |> List.map (Html.map liftMsg)
33+ , update = \msg model -> update callbacks liftMsg msg model
34+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
35+ , title = always [ text "Enter master password" ]
36+ , event = \model _ -> model
37+ }
3338
3439
35−init : Callbacks emsg -> { server : String, login : String } -> Model emsg
36−init callbacks { server, login } =
37− { server = server
38− , email = login
39− , password = ""
40− , callbacks = callbacks
41− }
40+init : { server : String, login : String } -> ( Model, Cmd Msg )
41+init { server, login } =
42+ ( { server = server
43+ , email = login
44+ , password = ""
45+ }
46+ , Cmd.none
47+ )
4248
4349
44−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
45−update msg model =
50+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
51+update { submit, reset } _ msg model =
4652 case msg of
4753 UpdatePassword password ->
48− ( { model | password = password }, Cmd.none )
54+ ( Ok { model | password = password }, Cmd.none )
4955
5056 Submit ->
51− ( model, pureCmd (model.callbacks.submit { password = model.password }) )
57+ ( Ok model, pureCmd (submit { password = model.password }) )
5258
5359 Reset ->
54− ( model, pureCmd model.callbacks.reset )
60+ ( Ok model, pureCmd reset )
61+
62+
63+subscriptions : Model -> Sub Msg
64+subscriptions _ =
65+ Sub.none
5566
5667
57−view : Model emsg -> List (Html Msg)
68+view : Model -> List (Html Msg)
5869view model =
5970 [ alignRight [ button [ Attr.class "p-button--negative", Ev.onClick Reset ] [ text "Log out" ] ]
6071 , table [ Attr.class "p-table--mobile-card u-no-margin" ]
frontend/Pages/SecondFactor.elmdeleted−91
@@ -1,91 +0,0 @@
1−module Pages.SecondFactor exposing
2− ( Callbacks
3− , Model
4− , Msg
5− , init
6− , title
7− , update
8− , view
9− )
10−
11−import Bridge
12−import Html exposing (..)
13−import Html.Attributes as Attr
14−import Html.Events as Ev
15−import Logic.SecondFactor exposing (secondFactorName)
16−import Utils exposing (..)
17−
18−
19−type alias Callbacks msg =
20− { submit : { token : String, remember : Bool } -> msg
21− }
22−
23−
24−type Msg
25− = Submit
26− | UpdateValue String
27− | UpdateRemember Bool
28−
29−
30−type alias Model msg =
31− { token : String
32− , remember : Bool
33− , factorType : Bridge.TwoFactorProviderType
34− , callbacks : Callbacks msg
35− }
36−
37−
38−init :
39− { provider : Bridge.TwoFactorProviderType
40− , callbacks : Callbacks msg
41− }
42− -> Model msg
43−init { provider, callbacks } =
44− { token = ""
45− , remember = True
46− , factorType = provider
47− , callbacks = callbacks
48− }
49−
50−
51−title : Model msg -> String
52−title { factorType } =
53− secondFactorName factorType
54−
55−
56−update : Model emsg -> Msg -> ( Model emsg, Maybe emsg )
57−update model msg =
58− case msg of
59− Submit ->
60− ( model, model.callbacks.submit { token = model.token, remember = model.remember } |> Just )
61−
62− UpdateValue token ->
63− ( { model | token = token }, Nothing )
64−
65− UpdateRemember remember ->
66− ( { model | remember = remember }, Nothing )
67−
68−
69−view : Model emsg -> Html Msg
70−view { factorType, token, remember } =
71− form [ Ev.onSubmit Submit ]
72− [ label [] [ secondFactorName factorType ++ " code" |> text ]
73− , input
74− [ Attr.type_ "number"
75− , Attr.value token
76− , Attr.required True
77− , Ev.onInput UpdateValue
78− , Attr.attribute "autocomplete" "one-time-code"
79− ]
80− []
81− , label []
82− [ input
83− [ Attr.type_ "checkbox"
84− , Attr.checked remember
85− , Ev.onCheck UpdateRemember
86− ]
87− []
88− , text "Remember this device"
89− ]
90− , alignRight [ button [ Attr.type_ "submit" ] [ text "Submit" ] ]
91− ]
frontend/Pages/SecondFactorSelect.elmdeleted−164
@@ -1,164 +0,0 @@
1−module Pages.SecondFactorSelect exposing
2− ( Callbacks
3− , Model
4− , Msg
5− , init
6− , main
7− , title
8− , update
9− , view
10− )
11−
12−import Bridge
13−import Element as El
14−import Element.Font as Font
15−import Element.Input as Input
16−import Html
17−import Html.Attributes as Attr
18−import Logic.SecondFactor exposing (secondFactorName)
19−import Page exposing (..)
20−import Utils exposing (..)
21−
22−
23−main : Program () (Model (Maybe Msg)) (Maybe Msg)
24−main =
25− Page.page
26− { init =
27− init { selectFactor = always Nothing }
28− [ Bridge.Duo
29− , Bridge.Email
30− ]
31− , view = view
32− , update = update
33− }
34−
35−
36−type alias Model emsg =
37− { availableFactors : List Bridge.TwoFactorProviderType
38− , callbacks : Callbacks emsg
39− }
40−
41−
42−type Msg
43− = SelectFactor Bridge.TwoFactorProviderType
44−
45−
46−type alias Callbacks emsg =
47− { selectFactor : Bridge.TwoFactorProviderType -> emsg
48− }
49−
50−
51−title : String
52−title =
53− "Second factor"
54−
55−
56−init : Callbacks emsg -> List Bridge.TwoFactorProviderType -> Model emsg
57−init callbacks providers =
58− { availableFactors = providers
59− , callbacks = callbacks
60− }
61−
62−
63−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
64−update msg model =
65− case msg of
66− SelectFactor factor ->
67− ( model, model.callbacks.selectFactor factor |> pureCmd )
68−
69−
70−view : Model emsg -> List (Html.Html Msg)
71−view { availableFactors } =
72− let
73− ( supportedProviders, unsupportedProviders ) =
74− List.partition providerIsSupported availableFactors
75− in
76− [ El.layout [ El.padding 8 ]
77− (El.column
78− [ El.width El.fill
79− , El.spacing 8
80− ]
81− ([ if List.isEmpty supportedProviders then
82− El.column []
83− [ El.paragraph []
84− [ El.text "Unfortunately, your account has no compatible 2FA providers."
85− ]
86− , El.el
87− [ El.height (El.px 16)
88− ]
89− El.none
90− ]
91−
92− else
93− El.column [ El.centerX ]
94− (supportedProviders
95− |> List.map
96− (\factor ->
97− Input.button [ El.centerX ]
98− { label =
99− Html.button [ Attr.class "p-button" ] [ secondFactorName factor |> Html.text ]
100− |> El.html
101− , onPress = SelectFactor factor |> Just
102− }
103− )
104− )
105− ]
106− ++ (if List.isEmpty unsupportedProviders |> not then
107− [ Html.div
108− [ Attr.class "p-notification--information"
109− ]
110− [ Html.div [ Attr.class "p-notification__content" ]
111− [ Html.h5 [ Attr.class "p-notification__title" ]
112− [ Html.text "The following 2FA providers are not supported"
113− ]
114− , Html.p [ Attr.class "notification__message" ]
115− [ Html.ul []
116− (List.map
117− (secondFactorName >> Html.text >> List.singleton >> Html.li [])
118− unsupportedProviders
119− )
120− ]
121− ]
122− ]
123− |> El.html
124− |> List.singleton
125− |> El.paragraph
126− [ Font.size 16
127− , El.width El.fill
128− ]
129− ]
130−
131− else
132− []
133− )
134− )
135− )
136− ]
137−
138−
139−providerIsSupported : Bridge.TwoFactorProviderType -> Bool
140−providerIsSupported provider =
141− case provider of
142− Bridge.Authenticator ->
143− True
144−
145− Bridge.Duo ->
146− False
147−
148− Bridge.Email ->
149− True
150−
151− Bridge.OrganizationDuo ->
152− False
153−
154− Bridge.Remember ->
155− False
156−
157− Bridge.U2f ->
158− False
159−
160− Bridge.WebAuthn ->
161− False
162−
163− Bridge.Yubikey ->
164− False
img/donate.pngdeleted

Binary file not shown.

index.html+1−1
@@ -7,7 +7,7 @@
77 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
88 <link rel="stylesheet" href="vanilla/dist/style.css">
99 <link rel="stylesheet" href="style.css">
10− <title>Warden</title>
10+ <title>Test.</title>
1111 <script src="elm.js"></script>
1212</head>
1313
manifest.json+6−6
@@ -1,15 +1,15 @@
11{
22 "name": "soy.iko.warden",
3− "description": "A simple Bitwarden client",
3+ "description": "A simple bitwarden client",
44 "architecture": "@CLICK_ARCH@",
55 "title": "Warden",
66 "hooks": {
7− "warden": {
8− "apparmor": "warden.apparmor",
9− "desktop": "warden.desktop"
7+ "appname": {
8+ "apparmor": "appname.apparmor",
9+ "desktop": "appname.desktop"
1010 }
1111 },
12− "version": "1.2.4",
13− "maintainer": "Ilia Kostiuchenko <mail@iko.soy>",
12+ "version": "1.0.0",
13+ "maintainer": "Ilia Kostiuchenko <email@iko.soy>",
1414 "framework": "@CLICK_FRAMEWORK@"
1515}
package-lock.jsondeleted−959
@@ -1,959 +0,0 @@
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
9999-------------------------------
100100-}
101101let upstream =
102− https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20220808/packages.dhall
103− sha256:60eee64b04ca0013fae3e02a69fc3b176105c6baa2f31865c67cd5f881a412fd
102+ https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220706/packages.dhall
103+ sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031
104104
105105in upstream
106106 with argonaut-aeson-generic =
@@ -125,7 +125,7 @@ in upstream
125125 ]
126126 , repo =
127127 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
128− , version = "3e388500811ef4b6718fc776789f3be050ad7b57"
128+ , version = "purescript-0.14"
129129 }
130130 with untagged-union =
131131 { dependencies =
@@ -147,7 +147,7 @@ in upstream
147147 }
148148 with literals =
149149 { repo = "https://github.com/ilyakooo0/purescript-literals.git"
150− , version = "6875fb28026595cfb780318305a77e79b098bb01"
150+ , version = "iko/_null"
151151 , dependencies =
152152 [ "assert"
153153 , "effect"
registry.datdeleted

Binary file not shown.

scratchpad/password_generator.html+1−16
@@ -5,7 +5,7 @@
55 <meta charset="UTF-8">
66 <meta http-equiv="X-UA-Compatible" content="IE=edge">
77 <meta name="viewport" content="width=device-width, initial-scale=1.0">
8− <link rel="stylesheet" href="../vanilla/dist/style.css">
8+ <link rel="stylesheet" href="../vanilla/style.css">
99 <link rel="stylesheet" href="../style.css">
1010 <title>Password generator</title>
1111
@@ -20,14 +20,6 @@
2020
2121<body>
2222
23− <div class="select-second-factor-container">
24− <button class="p-button">Hello</button>
25− <button class="p-button">Bar</button>
26− <button class="p-button">Bar</button>
27− <button class="p-button">Bardi</button>
28− <button class="p-button">Wolrd</button>
29− </div>
30−
3123 <div class="password-generator-group">
3224 <p>Password length</p>
3325 <input type="number" class="p-slider__input">
@@ -43,13 +35,6 @@
4335 <input type="number" class="p-slider__input">
4436 </div>
4537
46− <form action="">
47− <label><input type="checkbox" name="" id="">Hahahah</label>
48− <input type="checkbox">
49−
50− </form>
51−
52−
5338
5439 <input type="range" min="0" max="100" value="50" step="1">
5540 <input type="range" min="0" max="100" value="50" step="1"
spago-packages.nixdeleted−1102
@@ -1,1102 +0,0 @@
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−5
@@ -28,17 +28,12 @@ to generate this file without the comments in this block.
2828 , "either"
2929 , "exceptions"
3030 , "foldable-traversable"
31− , "foreign"
3231 , "functions"
33− , "integers"
34− , "literals"
3532 , "maybe"
3633 , "newtype"
3734 , "nullable"
38− , "numbers"
3935 , "orders"
4036 , "prelude"
41− , "random"
4237 , "refs"
4338 , "run"
4439 , "strings"
style.css−7
@@ -132,13 +132,6 @@ textarea {
132132 bottom: 0;
133133}
134134
135−.select-second-factor-container {
136− display: flex;
137− flex-direction: column;
138− align-items: center;
139− justify-content: center;
140−}
141−
142135.p-tabs__list {
143136 overflow: initial;
144137}
tests/package-lock.jsondeleted−73
@@ -1,73 +0,0 @@
1−{
2− "name": "warden-tests",
3− "version": "1.0.0",
4− "lockfileVersion": 2,
5− "requires": true,
6− "packages": {
7− "": {
8− "name": "warden-tests",
9− "version": "1.0.0",
10− "devDependencies": {
11− "@playwright/test": "^1.27.1"
12− }
13− },
14− "node_modules/@playwright/test": {
15− "version": "1.27.1",
16− "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.27.1.tgz",
17− "integrity": "sha512-mrL2q0an/7tVqniQQF6RBL2saskjljXzqNcCOVMUjRIgE6Y38nCNaP+Dc2FBW06bcpD3tqIws/HT9qiMHbNU0A==",
18− "dev": true,
19− "dependencies": {
20− "@types/node": "*",
21− "playwright-core": "1.27.1"
22− },
23− "bin": {
24− "playwright": "cli.js"
25− },
26− "engines": {
27− "node": ">=14"
28− }
29− },
30− "node_modules/@types/node": {
31− "version": "18.11.9",
32− "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
33− "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
34− "dev": true
35− },
36− "node_modules/playwright-core": {
37− "version": "1.27.1",
38− "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.27.1.tgz",
39− "integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==",
40− "dev": true,
41− "bin": {
42− "playwright": "cli.js"
43− },
44− "engines": {
45− "node": ">=14"
46− }
47− }
48− },
49− "dependencies": {
50− "@playwright/test": {
51− "version": "1.27.1",
52− "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.27.1.tgz",
53− "integrity": "sha512-mrL2q0an/7tVqniQQF6RBL2saskjljXzqNcCOVMUjRIgE6Y38nCNaP+Dc2FBW06bcpD3tqIws/HT9qiMHbNU0A==",
54− "dev": true,
55− "requires": {
56− "@types/node": "*",
57− "playwright-core": "1.27.1"
58− }
59− },
60− "@types/node": {
61− "version": "18.11.9",
62− "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
63− "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
64− "dev": true
65− },
66− "playwright-core": {
67− "version": "1.27.1",
68− "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.27.1.tgz",
69− "integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==",
70− "dev": true
71− }
72− }
73−}
tests/package.jsondeleted−9
@@ -1,9 +0,0 @@
1−{
2− "name": "warden-tests",
3− "version": "1.0.0",
4− "scripts": {},
5− "keywords": [],
6− "devDependencies": {
7− "@playwright/test": "^1.27.1"
8− }
9−}
tests/playwright.config.tsdeleted−65
@@ -1,65 +0,0 @@
1−import type { PlaywrightTestConfig } from '@playwright/test';
2−import { devices } from '@playwright/test';
3−
4−/**
5− * See https://playwright.dev/docs/test-configuration.
6− */
7−const config: PlaywrightTestConfig = {
8− testDir: './src',
9− /* Maximum time one test can run for. */
10− timeout: 30 * 1000,
11− expect: {
12− /**
13− * Maximum time expect() should wait for the condition to be met.
14− * For example in `await expect(locator).toHaveText();`
15− */
16− timeout: 5000
17− },
18− /* Run tests in files in parallel */
19− fullyParallel: false,
20− /* Fail the build on CI if you accidentally left test.only in the source code. */
21− forbidOnly: !!process.env.CI,
22− /* Retry on CI only */
23− retries: 0,
24− /* Opt out of parallel tests on CI. */
25− workers: 1,
26− /* Reporter to use. See https://playwright.dev/docs/test-reporters */
27− reporter: 'html',
28− /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
29− use: {
30− /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
31− actionTimeout: 0,
32− /* Base URL to use in actions like `await page.goto('/')`. */
33− // baseURL: 'http://localhost:3000',
34−
35− /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
36− trace: 'on-first-retry',
37− },
38−
39− /* Configure projects for major browsers */
40− projects: [
41− {
42− name: 'Pixel 3',
43− use: {
44− ...devices['Pixel 3'],
45− launchOptions: {
46− args: ["--disable-web-security"],
47− },
48− headless: process.env.RECORD_TEST !== "true",
49− }
50− },
51−
52− ],
53−
54− /* Folder for test artifacts such as screenshots, videos, traces, etc. */
55− outputDir: 'test-results/',
56−
57− /* Run your local dev server before starting the tests */
58− webServer: {
59− command: './vaultwarden/run.sh',
60− port: 8000,
61− },
62−
63−};
64−
65−export default config;
tests/src/test.spec.tsdeleted−257
@@ -1,257 +0,0 @@
1−import { test, expect, Page } from '@playwright/test';
2−
3−async function logIn(page: Page) {
4− await page.locator('input[type="url"]').tap();
5− await page.locator('input[type="url"]').press('Control+a');
6− await page.locator('input[type="url"]').fill('http://localhost:8000');
7− await page.locator('input[type="email"]').tap();
8− await page.locator('input[type="email"]').fill('test@example.com');
9− await page.locator('input[type="password"]').tap();
10− await page.locator('input[type="password"]').fill('Password123');
11− await page.getByRole('button', { name: 'Log in' }).tap();
12− await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "visible" })
13− await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "hidden" })
14−}
15−
16−async function fillInLoginCipher(page: Page) {
17− await page.locator('p:has-text("Entry name") input[type="text"]').tap();
18− await page.locator('p:has-text("Entry name") input[type="text"]').fill('Test login entry');
19− await page.locator('p:has-text("Username") input[type="text"]').tap();
20− await page.locator('p:has-text("Username") input[type="text"]').fill('login_entry_username');
21− await page.locator('input[type="text"]').nth(2).tap();
22− await page.locator('input[type="text"]').nth(2).fill('Password123');
23− await page.locator('input[type="url"]').tap();
24− await page.locator('input[type="url"]').fill('https://example.com');
25−}
26−
27−async function fillInCardCipher(page: Page) {
28− await page.locator('p:has-text("Entry name") input[type="text"]').tap();
29− await page.locator('p:has-text("Entry name") input[type="text"]').fill('Test card');
30− await page.locator('p:has-text("Card number") input[type="text"]').tap();
31− await page.locator('p:has-text("Card number") input[type="text"]').fill('1234567890123456');
32− await page.locator('p:has-text("Expiration date /") input[type="text"]').first().tap();
33− await page.locator('p:has-text("Expiration date /") input[type="text"]').first().fill('01');
34− await page.locator('p:has-text("Expiration date /") input[type="text"]').nth(1).tap();
35− await page.locator('p:has-text("Expiration date /") input[type="text"]').nth(1).fill('01');
36− await page.locator('p:has-text("CVV") input[type="text"]').tap();
37− await page.locator('p:has-text("CVV") input[type="text"]').fill('123');
38− await page.locator('p:has-text("Cardholder name") input[type="text"]').tap();
39− await page.locator('p:has-text("Cardholder name") input[type="text"]').fill('Sampel Palnet');
40−}
41−
42−async function fillInNoteCipher(page: Page) {
43− await page.locator('input[type="text"]').tap();
44− await page.locator('input[type="text"]').fill('Secret note');
45− await page.locator('textarea').tap();
46− await page.locator('textarea').fill('This is a very secret note');
47−}
48−
49−async function fillInContractCipher(page: Page) {
50− await page.locator('p:has-text("Entry name") input[type="text"]').tap();
51− await page.locator('p:has-text("Entry name") input[type="text"]').fill('John Appleseed');
52− await page.locator('p:has-text("First name") input[type="text"]').tap();
53− await page.locator('p:has-text("First name") input[type="text"]').fill('John');
54− await page.locator('p:has-text("Middle name") input[type="text"]').tap();
55− await page.locator('p:has-text("Middle name") input[type="text"]').fill('Mathews');
56− await page.locator('p:has-text("Last name") input[type="text"]').tap();
57− await page.locator('p:has-text("Last name") input[type="text"]').fill('Appleseed');
58− await page.locator('p:has-text("Username") input[type="text"]').tap();
59− await page.locator('p:has-text("Username") input[type="text"]').fill('jappleseed');
60− await page.locator('p:has-text("Company") input[type="text"]').tap();
61− await page.locator('p:has-text("Company") input[type="text"]').fill('Very good building company');
62− await page.locator('p:has-text("Social security number") input[type="number"]').tap();
63− await page.locator('p:has-text("Social security number") input[type="number"]').fill('123456789');
64− await page.locator('p:has-text("Passport number") input[type="number"]').tap();
65− await page.locator('p:has-text("Passport number") input[type="number"]').fill('123456798');
66− await page.locator('p:has-text("License Number") input[type="number"]').tap();
67− await page.locator('p:has-text("License Number") input[type="number"]').fill('12345');
68− await page.locator('input[type="email"]').tap();
69− await page.locator('input[type="email"]').fill('john@example.com');
70− await page.locator('input[type="tel"]').tap();
71− await page.locator('input[type="tel"]').fill('123456789');
72− await page.locator('p:has-text("Address line 1") input[type="text"]').tap();
73− await page.locator('p:has-text("Address line 1") input[type="text"]').fill('Abc def');
74− await page.locator('p:has-text("Address line 2") input[type="text"]').tap();
75− await page.locator('p:has-text("Address line 2") input[type="text"]').fill('Ghijk Lmn');
76−}
77−
78−async function screenshot(page: Page) {
79− await page.waitForLoadState("networkidle")
80−
81− if (await page.locator('div.floating-notifications').isVisible()) {
82− await page.locator("button.p-notification__close").tap()
83− await screenshot(page)
84− } else if (await page.locator('.p-icon--spinner.u-animation--spin.spinner').isVisible()) {
85− await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "hidden" })
86− await screenshot(page)
87− } else {
88− await expect.soft(page).toHaveScreenshot({ animations: "disabled", fullPage: true, maxDiffPixelRatio: 0.01 })
89− }
90−}
91−
92−async function waitForNotification(page: Page) {
93− await page.locator('div.floating-notifications').waitFor({ state: "visible" })
94−}
95−
96−
97−/**
98− * Screenshots the very first (last edited) entry.
99− */
100−function cipherPage(name: string) {
101− test(name + " cipher page", async ({ page }) => {
102− await logIn(page)
103−
104− await page.locator('.cipher-row').first().tap()
105−
106− await screenshot(page)
107− })
108−}
109−
110−test.beforeEach(async ({ page }) => {
111− await page.goto(`file://${__dirname}/../../dist/index.html`);
112− await page.addStyleTag({ content: '.hide-in-test {opacity: 0;}' })
113−})
114−
115−if (process.env.RECORD_TEST === "true") {
116−
117− test('record', async ({ page }) => {
118− await logIn(page)
119− await page.pause()
120− })
121−
122−} else {
123− test('Login page', async ({ page }) => {
124− await screenshot(page)
125− });
126−
127− test('Empty ciphers page', async ({ page }) => {
128− await logIn(page)
129−
130− await screenshot(page)
131− })
132−
133− test('Login entry page', async ({ page }) => {
134− await logIn(page)
135−
136− await page.getByRole('button').nth(2).tap();
137− await page.getByRole('button', { name: 'Login' }).tap();
138−
139− await screenshot(page)
140−
141− await fillInLoginCipher(page)
142−
143− await screenshot(page)
144−
145− await page.getByRole('heading').locator('span').getByRole('button').tap();
146−
147− await waitForNotification(page)
148− });
149−
150− cipherPage("Login")
151−
152− test('Create Card entry page', async ({ page }) => {
153− await logIn(page)
154−
155− await page.getByRole('button').nth(2).tap();
156− await page.getByRole('button', { name: 'Card' }).tap();
157−
158− await screenshot(page)
159−
160− await fillInCardCipher(page)
161−
162− await screenshot(page)
163−
164− await page.getByRole('heading').locator('span').getByRole('button').tap();
165−
166− await waitForNotification(page)
167− })
168−
169− cipherPage("Card")
170−
171− test('Create Note entry page', async ({ page }) => {
172− await logIn(page)
173−
174− await page.getByRole('button').nth(2).tap();
175− await page.getByRole('button', { name: 'Note' }).tap();
176−
177− await screenshot(page)
178−
179− await fillInNoteCipher(page)
180−
181− await screenshot(page)
182−
183− await page.getByRole('heading').locator('span').getByRole('button').tap();
184−
185− await waitForNotification(page)
186− })
187−
188− cipherPage("Note")
189−
190− test('Create Contact entry page', async ({ page }) => {
191− await logIn(page)
192−
193− await page.getByRole('button').nth(2).tap();
194− await page.getByRole('button', { name: 'Contact' }).tap();
195−
196− await screenshot(page)
197−
198− await fillInContractCipher(page)
199−
200− await screenshot(page)
201−
202− await page.getByRole('heading').locator('span').getByRole('button').tap();
203−
204− await waitForNotification(page)
205− })
206−
207− cipherPage("Contact")
208−
209− test('Filled ciphers page', async ({ page }) => {
210− await logIn(page)
211− await screenshot(page)
212− })
213−
214− test('Side bar', async ({ page }) => {
215− await logIn(page)
216−
217− await page.getByRole('button').nth(1).tap();
218−
219− await screenshot(page)
220− })
221−
222− test('Passwords view', async ({ page }) => {
223− await logIn(page)
224−
225− await page.getByRole('button').nth(1).click();
226− await page.getByText('Passwords').click();
227−
228− await screenshot(page)
229− })
230−
231− test('Notes view', async ({ page }) => {
232− await logIn(page)
233−
234− await page.getByRole('button').nth(1).click();
235− await page.getByText('Notes').click();
236−
237− await screenshot(page)
238− })
239−
240− test('Cards view', async ({ page }) => {
241− await logIn(page)
242−
243− await page.getByRole('button').nth(1).click();
244− await page.getByText('Cards').click();
245−
246− await screenshot(page)
247− })
248−
249− test('Contacts view', async ({ page }) => {
250− await logIn(page)
251−
252− await page.getByRole('button').nth(1).click();
253− await page.getByText('Contacts').click();
254−
255− await screenshot(page)
256− })
257−}
tests/src/test.spec.ts-snapshots/Card-cipher-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

tests/vaultwarden/.envdeleted−3
@@ -1,3 +0,0 @@
1−WEB_VAULT_ENABLED=false
2−LOGIN_RATELIMIT_SECONDS=1
3−LOGIN_RATELIMIT_MAX_BURST=100000000
tests/vaultwarden/README.mddeleted−4
@@ -1,4 +0,0 @@
1−# Test vaultwarden setup
2−
3−login: `test@example.com`
4−password: `Password123`
tests/vaultwarden/data/db.sqlite3deleted

Binary file not shown.

tests/vaultwarden/data/db.sqlite3-shmdeleted

Binary file not shown.

tests/vaultwarden/data/db.sqlite3-waldeleted

No changes to the content.

tests/vaultwarden/data/rsa_key.pemdeleted−27
@@ -1,27 +0,0 @@
1−-----BEGIN RSA PRIVATE KEY-----
2−MIIEowIBAAKCAQEArVVTdkrmMbDxyGxGY73Fse9Du7DplgSBPQ5uyOnAsqf6pa1U
3−jjWwTV7Xqzp0z7k32WC64P0eGdacTQGZiFUQxTVm6bJ4TZ8bh4fz51tB4npCAzpr
4−8EcgGRzZT+cA1GQOG7jDqv8w9edWWGdPr6mx1ENvnpXtv60tvnwfsioAUSnx3SC6
5−0oTVCxj1InyneUDXCk/UqBE1wmwl8aF5U3aOc42qcELAgr6i0aCsK1LQjH+SOREh
6−tRQgpDF17IdYAxdYUTyinqrYJnoo5kQXeC/4QoZIsDi1empas7pXwrDlVYtjNK2T
7−Oht6dWUnffc2oTUCNY+ATZ6Nsu4zItEM7yQo/QIDAQABAoIBABUX/q21JLlkXrA0
8−2QYXWKE0TGphd7O9fZrYEExcAgrb3WlfJBygVQG9aoKYfRGwaScHmgZ6VzjuwJuw
9−d6Y7L+zH+Gu1P+W2WKvQNDLyDJ0KotDwwaT+it3zgFC0fIncWUkvXYSIRMWQLxZY
10−ED8A6Da8NC+48DN02E3HnjXY3awCusn9fQkPLeparsdyrvz5s8lnYYQZ6u7FQeD9
11−gM5SUP8WVLtPl4KBidJvHl4qPDAxnu98cZEoffbWIAsg3wvKNZL9yE21ZGWdExxA
12−/czWYO4ckuGY7Tb0Y+MiRI5kBM4VbCzwVfZTLRPXnKddiL7QpLMNmYpHzEcMZut4
13−z3vcdnMCgYEA5ZIzaltIEfL8zmX76pIPlyBs5n98RaagCIxQyURNRSU4H6Sr6yW2
14−CNAszcn6iAxiteXkPUkWmPXE4BFt0ifKgecPQIDk0spaVu2khRPMwUMyR0PfwXfY
15−2fqT0zMZX0MB3VPxnDTY9sAvTi2FsskL9j+Pil7U8bYaixzdc2H+LcsCgYEAwUm2
16−5A1eyX8ojZM5xUaQ/ZAiW+9u0fI5UxJ9Q0SFUWn11LJyIoIvkw6puE10508e7UT1
17−rnYZc5v/Mr6E1oO2NGBHkOZdlfJMD346r2YAtm2Nt/HZuLZU38wyIZzg0FxS6lmZ
18−m1vTEoM6Ko5GVtjvLEst27ZZDgWRW7rJNyYEq1cCgYBm6ZJY9FCrkzUXRYqXziBl
19−DCklbzMDej21d0lj4FWPUK8HKGfVQxOGgeQD4K6JvJunlUYq3w/dRBecptOHaUUT
20−1MP2k5GAmSvEWkXtTkH0xS3N2rR84z/3/6ey/u450S0Nzeq6rsowW1kZf7i2u/+7
21−xSSQ0BA/xvXBNRBlACHU3wKBgQDAuYF0zqgOfY2v7HaT75KuYUMBLDHxgHYMEMl5
22−gx7zWJXXqL4JDonlNZ+gtwqzgL6zkijt9a/XCFpi/HhzdAz/GGkDX888rcWNRS26
23−qhhXzdCa8rlGZfcm1vG9djDrUMMUDJcOw3YoZVRgDlCwU8nW/ncRNjaBtqeDMTFk
24−nbHWLQKBgH0Xatwy6DVHDbU+GYcZBjyQP5zCz0JEjDE8hEhv/ioCfUK1rfjY58zU
25−oXDdn3GpFm6eFxcVxv9Ki4jViQDYAvAErBl0kMkVU6hvITPlMEyqBh89h65xUEnw
26−ttaX+Q2lKGwHsZT4RzA45XjO8kvBymY9siL2JQUMLB8wpu9RyTRj
27−-----END RSA PRIVATE KEY-----
tests/vaultwarden/data/rsa_key.pub.pemdeleted−9
@@ -1,9 +0,0 @@
1−-----BEGIN PUBLIC KEY-----
2−MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVVTdkrmMbDxyGxGY73F
3−se9Du7DplgSBPQ5uyOnAsqf6pa1UjjWwTV7Xqzp0z7k32WC64P0eGdacTQGZiFUQ
4−xTVm6bJ4TZ8bh4fz51tB4npCAzpr8EcgGRzZT+cA1GQOG7jDqv8w9edWWGdPr6mx
5−1ENvnpXtv60tvnwfsioAUSnx3SC60oTVCxj1InyneUDXCk/UqBE1wmwl8aF5U3aO
6−c42qcELAgr6i0aCsK1LQjH+SOREhtRQgpDF17IdYAxdYUTyinqrYJnoo5kQXeC/4
7−QoZIsDi1empas7pXwrDlVYtjNK2TOht6dWUnffc2oTUCNY+ATZ6Nsu4zItEM7yQo
8−/QIDAQAB
9−-----END PUBLIC KEY-----
tests/vaultwarden/run.shdeleted−13
@@ -1,13 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd "$(dirname "$0")"
6−
7−tmp="$(mktemp -d)"
8−
9−cp -r data .env "$tmp"
10−
11−cd "$tmp"
12−
13−nix run github:NixOS/nixpkgs?rev=d9a1414346059619d9e13ab93e749bbb82e5252a#vaultwarden
vanillanew file+1

Submodule changed.